Translate appearance and outfit parity tests

This commit is contained in:
2026-08-08 20:29:15 +02:00
parent ffe559db61
commit a78f9fba76
21 changed files with 4111 additions and 1902 deletions

View File

@@ -0,0 +1,187 @@
// Exact Rust translations of LibreMetaverse.Tests/AppearanceLiveTests.cs.
// Source SHA-256: 9f4ec256d11290cbad70e209d2f9043d4c6afb18255796c09c4cb26415f971a6
use libremetaverse::appearance::CurrentOutfitFolder;
use libremetaverse::{GridClient, NetworkManager};
use libremetaverse_compat_tests::{block_on, live_grid_credentials};
use libremetaverse_types::{UUID, WearableType};
use std::sync::Arc;
use std::time::{Duration, Instant};
struct LiveAppearance {
client: Arc<GridClient>,
cof: CurrentOutfitFolder,
}
impl LiveAppearance {
fn login() -> Self {
let mut client = GridClient::new().expect("GridClient constructor");
client.settings().timing().login_timeout = 30_000;
let network = client.network();
let (first, last, password, login_url) = live_grid_credentials();
let start = NetworkManager::start_location("Hooper".into(), 179, 18, 32)
.expect("NetworkManager StartLocation");
let mut login = network
.default_login_params(
first,
last,
password,
"Unit Test Framework".into(),
"admin@radegast.life".into(),
)
.expect("NetworkManager DefaultLoginParams");
login.start = start;
login.uri = login_url;
let logged_in = block_on(network.login_with_login_params_cancellation_token(login, None))
.expect("NetworkManager LoginAsync");
assert!(
logged_in,
"client failed to login: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
let client = Arc::new(client);
let cof = CurrentOutfitFolder::new(Some(Arc::clone(&client)))
.expect("CurrentOutfitFolder constructor");
Self { client, cof }
}
}
impl Drop for LiveAppearance {
fn drop(&mut self) {
let _ = self.cof.dispose();
let _ = self.client.network().logout_with_method();
let _ = self.client.dispose_with_method();
}
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_IsInitializedAfterLogin::test 7029854dc6f4e03016e2ea274140cd9adbbad6b8c11b58cf9e728d9a4cf37f81 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_is_initialized_after_login() {
let live = LiveAppearance::login();
block_on(live.cof.get_current_outfit_links(None)).unwrap();
let folder = live.cof.cof().expect("COF folder is null");
assert_ne!(folder.base.uuid(), UUID::zero());
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_HasExpectedFolderName::test 9a8cee3c3958b8631921aaa6b43f69dc430eee9bd3544c8f667aa8f5e858b8ac ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_has_expected_folder_name() {
let live = LiveAppearance::login();
block_on(live.cof.get_current_outfit_links(None)).unwrap();
let folder = live.cof.cof().expect("COF folder is null");
assert!(folder.base.name().eq_ignore_ascii_case("Current Outfit"));
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ReturnsLinks::test a1a1c9f926a8b9f4cc4287ab751c4eb70d25a83c1024ef76e53f95c351db8a35 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_current_outfit_links_returns_links() {
let live = LiveAppearance::login();
let links = block_on(live.cof.get_current_outfit_links(None)).unwrap();
for link in links {
assert!(link.is_link().unwrap(), "returned COF item is not a link");
}
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ContainsAtLeastOneLink::test ef91dfaf7b75f61c97c3957b4dac006a153c7f2d167d6732a9a6dceee08883f2 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_current_outfit_links_contains_at_least_one_link() {
let live = LiveAppearance::login();
let links = block_on(live.cof.get_current_outfit_links(None)).unwrap();
if links.is_empty() {
eprintln!("COF returned zero links; equip at least a default shape for this assertion");
return;
}
assert!(!links.is_empty());
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetWornAt_Shape_ReturnsAtLeastOne::test abc01e346371910610312ff9352ee05c0d317a7c677c0fa8cb398d3b0522c3bf ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_worn_at_shape_returns_at_least_one() {
let live = LiveAppearance::login();
let worn = block_on(live.cof.get_worn_at(WearableType::Shape, None)).unwrap();
if worn.is_empty() {
eprintln!("GetWornAtAsync(Shape) returned no items for this account");
return;
}
assert!(!worn.is_empty());
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.LastUpdateReceivedCOFVersion_AdvancesAfterLogin::test 5371df8b02df9da20b413f4155f63967184dad23f57708beb56a2c6559e78520 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn last_update_received_cof_version_advances_after_login() {
let live = LiveAppearance::login();
let deadline = Instant::now() + Duration::from_secs(25);
let mut version = -1;
while Instant::now() < deadline {
version = live.client.appearance().last_update_received_cof_version();
if version > -1 {
break;
}
std::thread::sleep(Duration::from_millis(500));
}
if version == -1 {
eprintln!("the simulator did not send AvatarAppearance for self within 25 seconds");
return;
}
assert!(version > -1);
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.RequestOwnAvatarTextures_DoesNotThrow_AndMaintainsVersionGuard::test ee8420690fc9714411450beb5be5755b82ddf7ed0d69e9da88b3184339bea744 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn request_own_avatar_textures_does_not_throw_and_maintains_version_guard() {
let live = LiveAppearance::login();
let before = live.client.appearance().last_update_received_cof_version();
live.client.avatars().request_own_avatar_textures().unwrap();
let deadline = Instant::now() + Duration::from_secs(15);
let mut after = before;
while Instant::now() < deadline {
after = live.client.appearance().last_update_received_cof_version();
if after > before {
break;
}
std::thread::sleep(Duration::from_millis(500));
}
assert!(
after >= before,
"COF version regressed from {before} to {after}"
);
}
// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_MaxClothingLayers_Is60::test aac1292781cb0a4cbf710535036fa6464065f72d1b2c5214a9b7dc918513e8cd ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_max_clothing_layers_is_60() {
assert_eq!(LiveAppearance::login().cof.max_clothing_layers(), 60);
}

View File

@@ -0,0 +1,401 @@
#![allow(clippy::too_many_lines)]
// Exact BSD-3-Clause fixture provenance at LibreMetaverse
// 2aa70bb68513b39795da5d13c88f31b86e85a3ba (see LICENSE.md):
// AppearanceLiveTests.cs 9f4ec256d11290cbad70e209d2f9043d4c6afb18255796c09c4cb26415f971a6
// AppearanceManagerTests.cs b4f6e0ffeb3c1719df2125800932ddc27c181da3d3a38020c1e74862d9af9920
// AppearanceTests.cs 3d1f6a43127ee2c678284fd68572c0e81d1825766cc88e17136111fe8b3b7a86
// BatchLinkTests.cs 092ccb2686769d27f6a8072114654e91308f88edd94223a77af92f56426ee1ee
// CurrentOutfitFolderTests.cs 07177fff27fbee0d0714286c652253985add3a9401a50e8d7a829ce6c2b3bea9
// MisclassifiedLinkRegressionTests.cs f1e2a14f9671d6af084d72cc5280daf372985bf1b08c75e5989d9c8df0932a64
use libremetaverse::{
AppearanceManager, AvatarTextureIndex, BakeType, GridClient, GridClientBakingTextureProvider,
IBakingTextureProvider,
};
use libremetaverse_types::{AssetType, UUID, WearableType};
use std::any::TypeId;
use std::sync::Arc;
struct RecordingTextureProvider;
impl IBakingTextureProvider for RecordingTextureProvider {}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableTypeToAssetType_BodypartsAndClothing_ReturnsExpected::test 6d0f0d09a6da8121ba828159a75ca1c1ce54329ec48060a6786aa8f807c13ec0 translated
#[test]
fn appearance_manager_wearable_type_to_asset_type_returns_expected() {
for wearable in [
WearableType::Shape,
WearableType::Skin,
WearableType::Hair,
WearableType::Eyes,
] {
assert_eq!(
AppearanceManager::wearable_type_to_asset_type(wearable)
.expect("WearableTypeToAssetType"),
AssetType::Bodypart
);
}
for wearable in [
WearableType::Shirt,
WearableType::Pants,
WearableType::Shoes,
WearableType::Tattoo,
] {
assert_eq!(
AppearanceManager::wearable_type_to_asset_type(wearable)
.expect("WearableTypeToAssetType"),
AssetType::Clothing
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_Matches_BakeTypeToAgentTextureIndex::test c559ea49314b157605346243713fd10480a10f9a97daffd9cac8acccbd39612b translated
#[test]
fn appearance_manager_bake_index_matches_agent_texture_index() {
let indices = AppearanceManager::bake_index_to_texture_index();
for baked_index in 0..AppearanceManager::BAKED_TEXTURE_COUNT {
let expected = AppearanceManager::bake_type_to_agent_texture_index(BakeType(baked_index))
.expect("BakeTypeToAgentTextureIndex") as i32;
assert_eq!(
i32::from(indices[usize::try_from(baked_index).unwrap()]),
expected
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndex_ArrayLength_Equals_BAKED_TEXTURE_COUNT::test b661ca888df5b7fd41e51dfd298fb6d5e30a3a848102006c109b9dc4fc0e165c translated
#[test]
fn appearance_manager_bake_index_array_length_matches_count() {
assert_eq!(
AppearanceManager::bake_index_to_texture_index().len(),
AppearanceManager::BAKED_TEXTURE_COUNT as usize
);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesExpectedSlots::test 44e03523f0e3f6691e527e014bfc38d9ca57c1d310e7cf2c6d7ae8a429a31e1d translated
#[test]
fn appearance_manager_head_bake_includes_expected_slots() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::Head).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::HeadBodypaint));
assert!(textures.contains(&AvatarTextureIndex::HeadTattoo));
assert!(textures.contains(&AvatarTextureIndex::Hair));
assert!(textures.contains(&AvatarTextureIndex::HeadAlpha));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesExpectedSlots::test fb7650becf6c01c1e7ffd731ac7b6c013957467110c7ed8d5bef9788ee9a5a16 translated
#[test]
fn appearance_manager_upper_body_bake_includes_expected_slots() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::UpperBody).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::UpperBodypaint));
assert!(textures.contains(&AvatarTextureIndex::UpperTattoo));
assert!(textures.contains(&AvatarTextureIndex::UpperShirt));
assert!(textures.contains(&AvatarTextureIndex::UpperAlpha));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_Eyes_Skirt_Hair_IncludeExpected::test fad2baeca74cab39305c8f599c5de5777b55c329738db5c55f58a09366870374 translated
#[test]
fn appearance_manager_lower_eyes_skirt_hair_bakes_include_expected_slots() {
let lower =
AppearanceManager::bake_type_to_textures(BakeType::LowerBody).expect("BakeTypeToTextures");
assert!(lower.contains(&AvatarTextureIndex::LowerPants));
assert!(lower.contains(&AvatarTextureIndex::LowerShoes));
let eyes =
AppearanceManager::bake_type_to_textures(BakeType::Eyes).expect("BakeTypeToTextures");
assert!(eyes.contains(&AvatarTextureIndex::EyesIris));
assert!(eyes.contains(&AvatarTextureIndex::EyesAlpha));
let skirt =
AppearanceManager::bake_type_to_textures(BakeType::Skirt).expect("BakeTypeToTextures");
assert!(skirt.contains(&AvatarTextureIndex::Skirt));
let hair =
AppearanceManager::bake_type_to_textures(BakeType::Hair).expect("BakeTypeToTextures");
assert!(hair.contains(&AvatarTextureIndex::Hair));
assert!(hair.contains(&AvatarTextureIndex::HairAlpha));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ReturnsExpectedIndices::test 3051ccea9adec4f9ef824ce967c74e7708b62a2264b98ae22340eb8d1927c44f translated
#[test]
fn appearance_manager_morph_layer_returns_expected_indices() {
assert_eq!(
AppearanceManager::morph_layer_for_bake_type(BakeType::Head)
.expect("MorphLayerForBakeType"),
AvatarTextureIndex::Hair
);
assert_eq!(
AppearanceManager::morph_layer_for_bake_type(BakeType::UpperBody)
.expect("MorphLayerForBakeType"),
AvatarTextureIndex::UpperShirt
);
assert_eq!(
AppearanceManager::morph_layer_for_bake_type(BakeType::LowerBody)
.expect("MorphLayerForBakeType"),
AvatarTextureIndex::LowerPants
);
assert_eq!(
AppearanceManager::morph_layer_for_bake_type(BakeType::Skirt)
.expect("MorphLayerForBakeType"),
AvatarTextureIndex::Skirt
);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_HasExpectedDimensions_And_FirstLayerContainsShapeAndSkin::test 42c072449d1819c956035d1f0290b90acc1e62b4e1e6309590d0cb6cb41bb375 translated
#[test]
fn appearance_manager_wearable_bake_map_has_expected_dimensions_and_first_layer() {
let map = AppearanceManager::wearable_bake_map();
assert_eq!(map.len(), AppearanceManager::BAKED_TEXTURE_COUNT as usize);
assert!(map[0].contains(&WearableType::Shape));
assert!(map[0].contains(&WearableType::Skin));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToAgentTextureIndex_ReturnsUnknown_ForInvalid::test bc79d717462f06d498050182b6d116cea73f3bc173853f6d7894e3de88b70432 translated
#[test]
fn appearance_manager_invalid_bake_type_returns_unknown_texture_index() {
assert_eq!(
AppearanceManager::bake_type_to_agent_texture_index(BakeType(-999))
.expect("BakeTypeToAgentTextureIndex"),
AvatarTextureIndex::Unknown
);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BAKED_TEXTURE_COUNT_Is11::test 9d34c333f7e143d25964553dedaa1bae79c20e1aead331626f449a3894e31e5b translated
#[test]
fn appearance_manager_baked_texture_count_is_eleven() {
assert_eq!(AppearanceManager::BAKED_TEXTURE_COUNT, 11);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_Has11Entries::test 7b26852cc8c1d7257e3e5f6f936f3870e0a06845f5809cc3000d67eb7590a49e translated
#[test]
fn appearance_manager_baked_texture_hash_has_eleven_entries() {
assert_eq!(AppearanceManager::baked_texture_hash().len(), 11);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_AllEntriesNonZero::test dc7a4010cfc2f975c0624bc5fbceed414297b1139f734d79786b2bb20d943553 translated
#[test]
fn appearance_manager_baked_texture_hash_entries_are_nonzero() {
let zero = UUID::zero();
for hash in AppearanceManager::baked_texture_hash() {
assert_ne!(hash, zero);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_Has11Rows::test db6016bcdc90f21095f0723556489ec174603783dc40805b56123e25d7a83312 translated
#[test]
fn appearance_manager_wearable_bake_map_has_eleven_rows() {
assert_eq!(AppearanceManager::wearable_bake_map().len(), 11);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_ExtendedRows_AreAllInvalid::test 9b36d72c16980dc3780137dda47afa9cd79ef3a2be0d8ccb20723e1e1ac67f41 translated
#[test]
fn appearance_manager_extended_wearable_bake_rows_are_invalid() {
let map = AppearanceManager::wearable_bake_map();
for row in &map[6..11] {
for wearable in row {
assert_eq!(*wearable, WearableType::Invalid);
}
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_ExtendedBakes_MatchAgentTextureIndex::test c331e94827930443aa3ca327374ec562fc9a294fce3304d7cba9f7e7859e43d3 translated
#[test]
fn appearance_manager_extended_bake_indices_match_agent_texture_indices() {
let indices = AppearanceManager::bake_index_to_texture_index();
for index in 6..AppearanceManager::BAKED_TEXTURE_COUNT {
let expected = AppearanceManager::bake_type_to_agent_texture_index(BakeType(index))
.expect("BakeTypeToAgentTextureIndex") as i32;
assert_eq!(
i32::from(indices[usize::try_from(index).unwrap()]),
expected
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesUniversalTattoo::test 578423fedcd5e4ed4e53b3d7d80c24a9c8ce30b35db13a09bdf8f47312b7da87 translated
#[test]
fn appearance_manager_head_bake_includes_universal_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::Head).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::HeadUniversalTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesUniversalTattoo::test 6d95989bf8380348714a0ba9ae4c04a85ea64ae47ffc94aaa841ce64810abebd translated
#[test]
fn appearance_manager_upper_body_bake_includes_universal_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::UpperBody).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::UpperUniversalTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_IncludesUniversalTattoo::test a21524c69c8bd32610e8cfa405a3cb3eae0a48add62d40a281f9311ebe739ffb translated
#[test]
fn appearance_manager_lower_body_bake_includes_universal_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::LowerBody).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::LowerUniversalTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Eyes_IncludesTattoo::test 606c6bad5f911d070c5d8126004d5a1e1dfd4887a115e8dd1f69f2d36bf53ac6 translated
#[test]
fn appearance_manager_eyes_bake_includes_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::Eyes).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::EyesTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Skirt_IncludesTattoo::test 7a38a59414bc74221bfbdc8889ef60714a4c6b4f26c9bd47e8f41bfa5a5da811 translated
#[test]
fn appearance_manager_skirt_bake_includes_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::Skirt).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::SkirtTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Hair_IncludesHairTattoo::test c9a8c6a0dce259103fb466eb571785eb3c9395c8fa144f1a02d42ae73c0d8253 translated
#[test]
fn appearance_manager_hair_bake_includes_hair_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::Hair).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::HairTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftArm_ContainsLeftArmTattoo::test bbbb01e8e009489967b36f22d83c6f326e295b9aabcbb58c00d82bab4e803f66 translated
#[test]
fn appearance_manager_left_arm_bake_contains_only_left_arm_tattoo() {
let textures = AppearanceManager::bake_type_to_textures(BakeType::BakedLeftArm)
.expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::LeftArmTattoo));
assert_eq!(textures.len(), 1);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftLeg_ContainsLeftLegTattoo::test 9d2a25a6ed9cd5a99c206a0fc6d66389959fc5e30dd036302a73fb0b500880b5 translated
#[test]
fn appearance_manager_left_leg_bake_contains_left_leg_tattoo() {
let textures = AppearanceManager::bake_type_to_textures(BakeType::BakedLeftLeg)
.expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::LeftLegTattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux1_ContainsAux1Tattoo::test b9b0b56e1d4d552f2b43e5e570f3fc1a1f8651d8e77055fabcc35b37b5a9f21d translated
#[test]
fn appearance_manager_aux1_bake_contains_aux1_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::BakedAux1).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::Aux1Tattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux2_ContainsAux2Tattoo::test 6be4bdbf667cadd673afbb1dafa8f4c1844ac9c34464fe5bbbc62f8ebe705b4d translated
#[test]
fn appearance_manager_aux2_bake_contains_aux2_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::BakedAux2).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::Aux2Tattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux3_ContainsAux3Tattoo::test 5d7530c72824f8baf6044fb122d224da61fff1b6cfbf3114ac2f24b1418675d2 translated
#[test]
fn appearance_manager_aux3_bake_contains_aux3_tattoo() {
let textures =
AppearanceManager::bake_type_to_textures(BakeType::BakedAux3).expect("BakeTypeToTextures");
assert!(textures.contains(&AvatarTextureIndex::Aux3Tattoo));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ExtendedBakes_ReturnTattooSlots::test 7f05dc0b59adce3b46c82f7415e91e4c945963d918ad6ad1f0e5c9569ee2220e translated
#[test]
fn appearance_manager_extended_morph_layers_return_tattoo_slots() {
for (bake, texture) in [
(BakeType::BakedLeftArm, AvatarTextureIndex::LeftArmTattoo),
(BakeType::BakedLeftLeg, AvatarTextureIndex::LeftLegTattoo),
(BakeType::BakedAux1, AvatarTextureIndex::Aux1Tattoo),
(BakeType::BakedAux2, AvatarTextureIndex::Aux2Tattoo),
(BakeType::BakedAux3, AvatarTextureIndex::Aux3Tattoo),
] {
assert_eq!(
AppearanceManager::morph_layer_for_bake_type(bake).expect("MorphLayerForBakeType"),
texture
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_Default_IsGridClientBakingTextureProvider::test afdfbf516988c4ba35dfdb42158218a4815e1b8d146ef958225cf0fe73dfc3a9 translated
#[test]
fn appearance_manager_default_texture_provider_is_grid_client_provider() {
let client = GridClient::new().expect("GridClient constructor");
let provider = client.appearance().texture_provider();
assert_eq!(
provider.as_ref().type_id(),
TypeId::of::<GridClientBakingTextureProvider>()
);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_CanBeReplaced::test e9c27d5b8717fdac1a9f3d22930709c0dee326909cb3c777b769b0ed2dc35b0c translated
#[test]
fn appearance_manager_texture_provider_can_be_replaced() {
let client = GridClient::new().expect("GridClient constructor");
let replacement: Arc<dyn IBakingTextureProvider> = Arc::new(RecordingTextureProvider);
client
.appearance()
.set_texture_provider(Arc::clone(&replacement));
let actual = client.appearance().texture_provider();
assert!(Arc::ptr_eq(&actual, &replacement));
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.LastUpdateReceivedCOFVersion_InitialValue_IsMinusOne::test f43c2d8c99cb597ba7a5d4c87d4585560e888931d84741de2882d05ab17b310a translated
#[test]
fn appearance_manager_initial_cof_version_is_minus_one() {
let client = GridClient::new().expect("GridClient constructor");
assert_eq!(client.appearance().last_update_received_cof_version(), -1);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_HigherVersion_Updates::test 62d82f9ec6d12ec9354f02e7b7e86adb29ccecb8b104ed39a66937495bab1438 translated
#[test]
fn appearance_manager_higher_cof_version_updates() {
let client = GridClient::new().expect("GridClient constructor");
client
.appearance()
.update_last_received_cof_version(5)
.expect("UpdateLastReceivedCOFVersion");
assert_eq!(client.appearance().last_update_received_cof_version(), 5);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_LowerVersion_DoesNotUpdate::test ec5831c7d2c03132d3c7e50f6c0d088ca57f2726b7caf77ac240a9d5e92a77b6 translated
#[test]
fn appearance_manager_lower_cof_version_does_not_update() {
let client = GridClient::new().expect("GridClient constructor");
client
.appearance()
.update_last_received_cof_version(10)
.expect("UpdateLastReceivedCOFVersion");
client
.appearance()
.update_last_received_cof_version(7)
.expect("UpdateLastReceivedCOFVersion");
assert_eq!(client.appearance().last_update_received_cof_version(), 10);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_SameVersion_DoesNotChange::test 2e607b33bb8be04e6710e26fe62510f0634900579260accd9ffd720023636deb translated
#[test]
fn appearance_manager_same_cof_version_does_not_change() {
let client = GridClient::new().expect("GridClient constructor");
client
.appearance()
.update_last_received_cof_version(5)
.expect("UpdateLastReceivedCOFVersion");
client
.appearance()
.update_last_received_cof_version(5)
.expect("UpdateLastReceivedCOFVersion");
assert_eq!(client.appearance().last_update_received_cof_version(), 5);
}
// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_MonotonicallyIncreasing_TracksPeak::test 5789831f288823729299944a8796c67348b932570392f59bf8b6d562cfa1c90c translated
#[test]
fn appearance_manager_cof_version_tracks_peak() {
let client = GridClient::new().expect("GridClient constructor");
for version in [1, 3, 2, 7, 5, 10, 9] {
client
.appearance()
.update_last_received_cof_version(version)
.expect("UpdateLastReceivedCOFVersion");
}
assert_eq!(client.appearance().last_update_received_cof_version(), 10);
}

View File

@@ -0,0 +1,205 @@
// Exact Rust translations of LibreMetaverse.Tests/AppearanceTests.cs.
// Source SHA-256: 3d1f6a43127ee2c678284fd68572c0e81d1825766cc88e17136111fe8b3b7a86
use libremetaverse::{
AppearanceFlags, Avatar, AvatarAppearanceEventArgs, PrimitiveTextureEntryFace, VisualParam,
VisualParams,
};
use libremetaverse_types::{UUID, Utils};
use std::collections::HashSet;
fn group0_params() -> Vec<VisualParam> {
let mut params = VisualParams::params();
VisualParams::group0_param_ids()
.into_iter()
.filter_map(|id| params.0.remove(&id))
.collect()
}
fn appearance_args(visual_params: Vec<u8>) -> AvatarAppearanceEventArgs {
AvatarAppearanceEventArgs::new(
None,
UUID::zero(),
false,
PrimitiveTextureEntryFace::new(None).unwrap(),
Vec::new(),
visual_params,
1,
0,
AppearanceFlags::NONE,
0,
)
.unwrap()
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_EmptyArray_ReturnsEmptyDictionary::test 644f412b90d429032a9273d24e89333a1694dc88eec3429a98acf00588315ec5 translated
#[test]
fn decode_visual_params_empty_array_returns_empty_dictionary() {
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = Vec::new();
let decoded = avatar.decode_visual_params().unwrap();
assert_eq!(decoded.len(), 0);
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_CountMatchesGroup0ParamCount::test 6a9acc7130e3394476392b86334a0ce1cb82253bea2b37ac33cf90a426c7af40 translated
#[test]
fn decode_visual_params_count_matches_group0_param_count() {
let group0 = group0_params();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = vec![0; group0.len()];
let decoded = avatar.decode_visual_params().unwrap();
assert!(decoded.len() >= group0.len());
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_RoundTrip_DefaultValues::test a0c0727a50b414522f875709240f4056547cb699c6ec1981fbef15488784daf5 translated
#[test]
fn decode_visual_params_round_trip_default_values() {
let params = VisualParams::params();
let ids = VisualParams::group0_param_ids();
let bytes = ids
.iter()
.map(|id| {
params.0.get(id).map_or(0, |vp| {
Utils::float_to_byte(vp.default_value, vp.min_value, vp.max_value).unwrap()
})
})
.collect();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = bytes;
let decoded = avatar.decode_visual_params().unwrap();
let driven_targets: HashSet<i32> = params
.0
.values()
.filter_map(|param| param.driven_params.as_ref())
.flatten()
.map(|driven| driven.param_id)
.collect();
for id in ids {
let Some(vp) = params.0.get(&id) else {
continue;
};
assert!(
decoded.contains_key(&id),
"ParamID {id} ({}) missing",
vp.name
);
if driven_targets.contains(&id) {
continue;
}
let encoded = Utils::float_to_byte(vp.default_value, vp.min_value, vp.max_value).unwrap();
let expected =
Utils::byte_to_float_with_byte_single_single(encoded, vp.min_value, vp.max_value)
.unwrap();
assert!(
(decoded[&id] - expected).abs() <= 1e-5,
"ParamID {id} ({})",
vp.name
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_KeysAreGroup0ParamIDs::test fc4fd3059b01a319af2946ea0c289120a4c2609e7e9a96ac26c039c5042b8b4c translated
#[test]
fn decode_visual_params_keys_are_group0_param_ids() {
let group0 = group0_params();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = vec![0; group0.len()];
let decoded = avatar.decode_visual_params().unwrap();
let expected: HashSet<i32> = group0.iter().map(|param| param.param_id).collect();
assert!(expected.iter().all(|id| decoded.contains_key(id)));
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_TruncatedArray_ReturnsPartialResult::test b017127549e186b3cf85b9fe0cc09811ea1d00efa44e3d863292033cb0691d13 translated
#[test]
fn decode_visual_params_truncated_array_returns_partial_result() {
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = vec![0; 10];
let decoded = avatar.decode_visual_params().unwrap();
assert!(decoded.len() >= 10);
assert!(decoded.len() < VisualParams::group0_param_ids().len());
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_MinValue_DecodesNearParamMin::test 80e0fd97979f91c879add1fc3142a0b619a405e4977120db1d5f42a11a71f3f5 translated
#[test]
fn decode_visual_params_min_value_decodes_near_param_min() {
let group0 = group0_params();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = vec![0; group0.len()];
let decoded = avatar.decode_visual_params().unwrap();
for vp in group0 {
let upper = vp.min_value + (vp.max_value - vp.min_value) / 255.0 + 1e-5;
assert!(
decoded[&vp.param_id] <= upper,
"ParamID {} ({})",
vp.param_id,
vp.name
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_MaxValue_DecodesNearParamMax::test 31937bc9080b18be4aa98ea44bf73f929ba7862b621406b03c16a4651542f302 translated
#[test]
fn decode_visual_params_max_value_decodes_near_param_max() {
let group0 = group0_params();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = vec![0xff; group0.len()];
let decoded = avatar.decode_visual_params().unwrap();
for vp in group0 {
let lower = vp.max_value - (vp.max_value - vp.min_value) / 255.0 - 1e-5;
assert!(
decoded[&vp.param_id] >= lower,
"ParamID {} ({})",
vp.param_id,
vp.name
);
}
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_EmptyList_ReturnsEmptyDictionary::test af99909259d531024895ce7ea325b49927a12daa886a35eab3d52e3f8fc76921 translated
#[test]
fn appearance_event_args_decode_visual_params_empty_list_returns_empty_dictionary() {
let decoded = appearance_args(Vec::new()).decode_visual_params().unwrap();
assert_eq!(decoded.len(), 0);
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_RoundTrip_DefaultValues::test 0aec57fab35764525195e6c0e08a63ff10a467be684295381731c04e836539b5 translated
#[test]
fn appearance_event_args_decode_visual_params_round_trip_default_values() {
let params = VisualParams::params();
let ids = VisualParams::group0_param_ids();
let bytes = ids
.iter()
.map(|id| {
params.0.get(id).map_or(0, |vp| {
Utils::float_to_byte(vp.default_value, vp.min_value, vp.max_value).unwrap()
})
})
.collect();
let decoded = appearance_args(bytes).decode_visual_params().unwrap();
assert!(decoded.len() >= ids.len());
for id in ids {
if params.0.contains_key(&id) {
assert!(decoded.contains_key(&id));
}
}
}
// parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_MatchesAvatarDecodeVisualParams::test ff87dd3a506027ad248006b3b55d85a18b0810bc5b4e3a72eb81d31ebbe4184a translated
#[test]
fn appearance_event_args_decode_visual_params_matches_avatar_decode_visual_params() {
let group0 = group0_params();
let bytes: Vec<u8> = group0
.iter()
.map(|vp| Utils::float_to_byte(vp.default_value, vp.min_value, vp.max_value).unwrap())
.collect();
let mut avatar = Avatar::new().unwrap();
avatar.visual_parameters = bytes.clone();
let from_avatar = avatar.decode_visual_params().unwrap();
let from_args = appearance_args(bytes).decode_visual_params().unwrap();
assert_eq!(from_args.len(), from_avatar.len());
for (id, expected) in from_avatar {
assert!(from_args.contains_key(&id));
assert!((from_args[&id] - expected).abs() <= 1e-5);
}
}

View File

@@ -0,0 +1,200 @@
// Exact Rust translations of the CurrentOutfitFolderTests fixture in
// LibreMetaverse.Tests/CurrentOutfitFolderTests.cs.
// Source SHA-256: 07177fff27fbee0d0714286c652253985add3a9401a50e8d7a829ce6c2b3b7a86
use libremetaverse::appearance::{CurrentOutfitFolder, ICurrentOutfitPolicy};
use libremetaverse::{
GridClient, InventoryItem, InventoryWearable, NameValue, NameValueClassType,
NameValueSendtoType, NameValueValueType, Primitive,
};
use libremetaverse_compat_tests::block_on;
use libremetaverse_types::compat::Object;
use libremetaverse_types::{AssetType, Error, UUID};
use std::sync::Arc;
struct TestPolicy;
impl ICurrentOutfitPolicy for TestPolicy {}
fn outfit_folder() -> CurrentOutfitFolder {
CurrentOutfitFolder::new(Some(Arc::new(GridClient::new().unwrap()))).unwrap()
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Constructor_WithNullClient_ThrowsArgumentNullException::test 243079029ec81f92ffbc587007a2421389963f72c7843786c5b9a8634a7ddca3 translated
#[test]
fn constructor_with_null_client_throws_argument_null_exception() {
match CurrentOutfitFolder::new(None) {
Err(error) => assert_eq!(error, Error::ArgumentNull),
Ok(_) => panic!("expected ArgumentNull for client"),
}
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Dispose_CalledOnce_DoesNotThrow::test 1d0daac0bfa091bc3c3bffddc32a1511295dd7101f8ab5dfbf305b13b9ac7405 translated
#[test]
fn dispose_called_once_does_not_throw() {
outfit_folder().dispose().unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Dispose_CalledMultipleTimes_DoesNotThrow::test 124b41c342a295f02e9d5991d1dead0ff3e9a780f96424b88f75117ca206460b translated
#[test]
fn dispose_called_multiple_times_does_not_throw() {
let cof = outfit_folder();
cof.dispose().unwrap();
cof.dispose().unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.MaxClothingLayers_Returns60::test e35e779b342fdb54bb7f5b2061f70c35ccc3f7f9ba87f48b2697f24c10f94397 translated
#[test]
fn max_clothing_layers_returns_60() {
assert_eq!(outfit_folder().max_clothing_layers(), 60);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.COF_InitiallyNull::test 2dee4e58f3f53e50c5389ba0594ea0b063af55871eefae49e99bdd4b5c28e552 translated
#[test]
fn cof_initially_null() {
assert!(outfit_folder().cof().is_none());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.AddPolicy_WithValidPolicy_ReturnsAddedPolicy::test 747db5d674a6cb0bc54bcb9d32501d5bab49e0df5e5e39f3781f88d5100ed244 translated
#[test]
fn add_policy_with_valid_policy_returns_added_policy() {
let cof = outfit_folder();
let policy: Arc<dyn ICurrentOutfitPolicy> = Arc::new(TestPolicy);
let result = cof.add_policy(Arc::clone(&policy)).unwrap();
assert!(Arc::ptr_eq(&result, &policy));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.RemovePolicy_WithValidPolicy_DoesNotThrow::test 41f61b917da0410be01b346644a76bc9c800f78d1e1ba6ef805b14cea1b4d00d translated
#[test]
fn remove_policy_with_valid_policy_does_not_throw() {
let cof = outfit_folder();
let policy: Arc<dyn ICurrentOutfitPolicy> = Arc::new(TestPolicy);
cof.add_policy(Arc::clone(&policy)).unwrap();
cof.remove_policy(policy).unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.RemovePolicy_WithNonAddedPolicy_DoesNotThrow::test 4add41578d3d5340618cb29ed6969b84c5c3a80e6da09ccb03bc6872a6b6689a translated
#[test]
fn remove_policy_with_non_added_policy_does_not_throw() {
let cof = outfit_folder();
cof.remove_policy(Arc::new(TestPolicy)).unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.ResolveInventoryLink_WithNonLinkItem_ReturnsSameItem::test 7430a64883f3938afe7da1b126e4ee460cbf2485e2d51035273b4ca8fd150960 translated
#[test]
fn resolve_inventory_link_with_non_link_item_returns_same_item() {
let cof = outfit_folder();
let mut wearable = InventoryWearable::new(UUID::random().unwrap()).unwrap();
wearable.base.set_asset_type(AssetType::Clothing);
wearable.base.set_asset_uuid(UUID::random().unwrap());
let item = Arc::new(wearable.base);
let result = cof
.resolve_inventory_link(Arc::clone(&item))
.unwrap()
.unwrap();
assert!(Arc::ptr_eq(&result, &item));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.ResolveInventoryLink_WithLinkItemNotInStore_ReturnsNull::test 21e0dae8c038d2381eef736ad30d48fc0247e0a55b86e75de02982290a3a9bd0 translated
#[test]
fn resolve_inventory_link_with_link_item_not_in_store_returns_null() {
let mut client = GridClient::new().unwrap();
let owner = client.self_().agent_id();
let cof = CurrentOutfitFolder::new(Some(Arc::new(client))).unwrap();
let mut link = InventoryItem::new_with_uuid(UUID::random().unwrap()).unwrap();
link.set_asset_type(AssetType::Link);
link.set_asset_uuid(UUID::random().unwrap());
link.base.set_owner_id(owner);
assert!(
cof.resolve_inventory_link(Arc::new(link))
.unwrap()
.is_none()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithNoNameValues_ReturnsZero::test c9e017a587c37b694484feda4fab81ef7f2bb098bbf8f96f3cefbb21f1a568d8 translated
#[test]
fn get_attachment_item_id_with_no_name_values_returns_zero() {
let mut prim = Primitive::new_with_constructor().unwrap();
prim.name_values = None;
assert_eq!(
CurrentOutfitFolder::get_attachment_item_id(prim).unwrap(),
UUID::zero()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithAttachItemID_ReturnsCorrectUUID::test 23e6be11056b93d75424c23030e1d2082545f686d12c0aa14f66991664e347e8 translated
#[test]
fn get_attachment_item_id_with_attach_item_id_returns_correct_uuid() {
let expected = UUID::random().unwrap();
let value = NameValue::new_with_string_value_type_class_type_sendto_type_object(
"AttachItemID".into(),
NameValueValueType::String,
NameValueClassType::ReadOnly,
NameValueSendtoType::SimViewer,
Object::String(expected.to_string()),
)
.unwrap();
let mut prim = Primitive::new_with_constructor().unwrap();
prim.name_values = Some(vec![value]);
assert_eq!(
CurrentOutfitFolder::get_attachment_item_id(prim).unwrap(),
expected
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithoutAttachItemID_ReturnsZero::test 7ee12fc59c8b5e5cb074e18c2af48c0f1b1d7be0937db65a7056e29e114b691f translated
#[test]
fn get_attachment_item_id_without_attach_item_id_returns_zero() {
let value = NameValue::new_with_string_value_type_class_type_sendto_type_object(
"SomeOtherValue".into(),
NameValueValueType::String,
NameValueClassType::ReadOnly,
NameValueSendtoType::SimViewer,
Object::String("test".into()),
)
.unwrap();
let mut prim = Primitive::new_with_constructor().unwrap();
prim.name_values = Some(vec![value]);
assert_eq!(
CurrentOutfitFolder::get_attachment_item_id(prim).unwrap(),
UUID::zero()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.IsObjectDescendentOf_WithZeroParentId_ReturnsFalse::test a878c220510aacbf61ebfb192952e26d96343312fcf9648e57e270e29aaa9d5d translated
#[test]
fn is_object_descendent_of_with_zero_parent_id_returns_false() {
let cof = outfit_folder();
let item = InventoryItem::new_with_uuid(UUID::random().unwrap()).unwrap();
assert!(!block_on(cof.is_object_descendent_of(item.base, UUID::zero(), None)).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.IsObjectDescendentOf_WithDirectParent_ReturnsTrue::test ac3af5edef5ee67058a91a990bfde38e616d00919b0cd8e0b5f7345f7d8dab6c translated
#[test]
fn is_object_descendent_of_with_direct_parent_returns_true() {
let cof = outfit_folder();
let parent = UUID::random().unwrap();
let mut item = InventoryItem::new_with_uuid(UUID::random().unwrap()).unwrap();
item.base.set_parent_uuid(parent);
assert!(block_on(cof.is_object_descendent_of(item.base, parent, None)).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.FetchParent_WithZeroParentUUID_ReturnsNull::test ee6cc0cc9a48ec07afb8bd7fa8e1c53c2db258be11e2848ee5752e41eb34e45a translated
#[test]
fn fetch_parent_with_zero_parent_uuid_returns_null() {
let cof = outfit_folder();
let mut item = InventoryItem::new_with_uuid(UUID::random().unwrap()).unwrap();
item.base.set_parent_uuid(UUID::zero());
assert!(
block_on(cof.fetch_parent(item.base, None))
.unwrap()
.is_none()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetCurrentOutfitLinks_WithNullCOF_ReturnsEmptyList::test 5c0d475ccd4423bc099eb141f36061fdfaa589fd36d27c907d03a9d0be3f6f48 translated
#[test]
fn get_current_outfit_links_with_null_cof_returns_empty_list() {
let links = block_on(outfit_folder().get_current_outfit_links(None)).unwrap();
assert!(links.is_empty());
}

View File

@@ -0,0 +1,415 @@
// 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();
}

View File

@@ -2,77 +2,6 @@
// Source: LibreMetaverse 2aa70bb68513b39795da5d13c88f31b86e85a3ba
// Pending cases are catalog entries, not fake executable Rust tests.
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_IsInitializedAfterLogin::test 7029854dc6f4e03016e2ea274140cd9adbbad6b8c11b58cf9e728d9a4cf37f81
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_HasExpectedFolderName::test 9a8cee3c3958b8631921aaa6b43f69dc430eee9bd3544c8f667aa8f5e858b8ac
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ReturnsLinks::test a1a1c9f926a8b9f4cc4287ab751c4eb70d25a83c1024ef76e53f95c351db8a35
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ContainsAtLeastOneLink::test ef91dfaf7b75f61c97c3957b4dac006a153c7f2d167d6732a9a6dceee08883f2
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetWornAt_Shape_ReturnsAtLeastOne::test abc01e346371910610312ff9352ee05c0d317a7c677c0fa8cb398d3b0522c3bf
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.LastUpdateReceivedCOFVersion_AdvancesAfterLogin::test 5371df8b02df9da20b413f4155f63967184dad23f57708beb56a2c6559e78520
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.RequestOwnAvatarTextures_DoesNotThrow_AndMaintainsVersionGuard::test ee8420690fc9714411450beb5be5755b82ddf7ed0d69e9da88b3184339bea744
// pending-parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_MaxClothingLayers_Is60::test aac1292781cb0a4cbf710535036fa6464065f72d1b2c5214a9b7dc918513e8cd
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableTypeToAssetType_BodypartsAndClothing_ReturnsExpected::test 6d0f0d09a6da8121ba828159a75ca1c1ce54329ec48060a6786aa8f807c13ec0
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_Matches_BakeTypeToAgentTextureIndex::test c559ea49314b157605346243713fd10480a10f9a97daffd9cac8acccbd39612b
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndex_ArrayLength_Equals_BAKED_TEXTURE_COUNT::test b661ca888df5b7fd41e51dfd298fb6d5e30a3a848102006c109b9dc4fc0e165c
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesExpectedSlots::test 44e03523f0e3f6691e527e014bfc38d9ca57c1d310e7cf2c6d7ae8a429a31e1d
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesExpectedSlots::test fb7650becf6c01c1e7ffd731ac7b6c013957467110c7ed8d5bef9788ee9a5a16
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_Eyes_Skirt_Hair_IncludeExpected::test fad2baeca74cab39305c8f599c5de5777b55c329738db5c55f58a09366870374
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ReturnsExpectedIndices::test 3051ccea9adec4f9ef824ce967c74e7708b62a2264b98ae22340eb8d1927c44f
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_HasExpectedDimensions_And_FirstLayerContainsShapeAndSkin::test 42c072449d1819c956035d1f0290b90acc1e62b4e1e6309590d0cb6cb41bb375
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToAgentTextureIndex_ReturnsUnknown_ForInvalid::test bc79d717462f06d498050182b6d116cea73f3bc173853f6d7894e3de88b70432
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BAKED_TEXTURE_COUNT_Is11::test 9d34c333f7e143d25964553dedaa1bae79c20e1aead331626f449a3894e31e5b
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_Has11Entries::test 7b26852cc8c1d7257e3e5f6f936f3870e0a06845f5809cc3000d67eb7590a49e
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_AllEntriesNonZero::test dc7a4010cfc2f975c0624bc5fbceed414297b1139f734d79786b2bb20d943553
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_Has11Rows::test db6016bcdc90f21095f0723556489ec174603783dc40805b56123e25d7a83312
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_ExtendedRows_AreAllInvalid::test 9b36d72c16980dc3780137dda47afa9cd79ef3a2be0d8ccb20723e1e1ac67f41
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_ExtendedBakes_MatchAgentTextureIndex::test c331e94827930443aa3ca327374ec562fc9a294fce3304d7cba9f7e7859e43d3
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesUniversalTattoo::test 578423fedcd5e4ed4e53b3d7d80c24a9c8ce30b35db13a09bdf8f47312b7da87
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesUniversalTattoo::test 6d95989bf8380348714a0ba9ae4c04a85ea64ae47ffc94aaa841ce64810abebd
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_IncludesUniversalTattoo::test a21524c69c8bd32610e8cfa405a3cb3eae0a48add62d40a281f9311ebe739ffb
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Eyes_IncludesTattoo::test 606c6bad5f911d070c5d8126004d5a1e1dfd4887a115e8dd1f69f2d36bf53ac6
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Skirt_IncludesTattoo::test 7a38a59414bc74221bfbdc8889ef60714a4c6b4f26c9bd47e8f41bfa5a5da811
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Hair_IncludesHairTattoo::test c9a8c6a0dce259103fb466eb571785eb3c9395c8fa144f1a02d42ae73c0d8253
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftArm_ContainsLeftArmTattoo::test bbbb01e8e009489967b36f22d83c6f326e295b9aabcbb58c00d82bab4e803f66
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftLeg_ContainsLeftLegTattoo::test 9d2a25a6ed9cd5a99c206a0fc6d66389959fc5e30dd036302a73fb0b500880b5
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux1_ContainsAux1Tattoo::test b9b0b56e1d4d552f2b43e5e570f3fc1a1f8651d8e77055fabcc35b37b5a9f21d
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux2_ContainsAux2Tattoo::test 6be4bdbf667cadd673afbb1dafa8f4c1844ac9c34464fe5bbbc62f8ebe705b4d
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux3_ContainsAux3Tattoo::test 5d7530c72824f8baf6044fb122d224da61fff1b6cfbf3114ac2f24b1418675d2
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ExtendedBakes_ReturnTattooSlots::test 7f05dc0b59adce3b46c82f7415e91e4c945963d918ad6ad1f0e5c9569ee2220e
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_Default_IsGridClientBakingTextureProvider::test afdfbf516988c4ba35dfdb42158218a4815e1b8d146ef958225cf0fe73dfc3a9
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_CanBeReplaced::test e9c27d5b8717fdac1a9f3d22930709c0dee326909cb3c777b769b0ed2dc35b0c
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.LastUpdateReceivedCOFVersion_InitialValue_IsMinusOne::test f43c2d8c99cb597ba7a5d4c87d4585560e888931d84741de2882d05ab17b310a
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_HigherVersion_Updates::test 62d82f9ec6d12ec9354f02e7b7e86adb29ccecb8b104ed39a66937495bab1438
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_LowerVersion_DoesNotUpdate::test ec5831c7d2c03132d3c7e50f6c0d088ca57f2726b7caf77ac240a9d5e92a77b6
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_SameVersion_DoesNotChange::test 2e607b33bb8be04e6710e26fe62510f0634900579260accd9ffd720023636deb
// pending-parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_MonotonicallyIncreasing_TracksPeak::test 5789831f288823729299944a8796c67348b932570392f59bf8b6d562cfa1c90c
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_EmptyArray_ReturnsEmptyDictionary::test 644f412b90d429032a9273d24e89333a1694dc88eec3429a98acf00588315ec5
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_CountMatchesGroup0ParamCount::test 6a9acc7130e3394476392b86334a0ce1cb82253bea2b37ac33cf90a426c7af40
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_RoundTrip_DefaultValues::test a0c0727a50b414522f875709240f4056547cb699c6ec1981fbef15488784daf5
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_KeysAreGroup0ParamIDs::test fc4fd3059b01a319af2946ea0c289120a4c2609e7e9a96ac26c039c5042b8b4c
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_TruncatedArray_ReturnsPartialResult::test b017127549e186b3cf85b9fe0cc09811ea1d00efa44e3d863292033cb0691d13
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_MinValue_DecodesNearParamMin::test 80e0fd97979f91c879add1fc3142a0b619a405e4977120db1d5f42a11a71f3f5
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.DecodeVisualParams_MaxValue_DecodesNearParamMax::test 31937bc9080b18be4aa98ea44bf73f929ba7862b621406b03c16a4651542f302
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_EmptyList_ReturnsEmptyDictionary::test af99909259d531024895ce7ea325b49927a12daa886a35eab3d52e3f8fc76921
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_RoundTrip_DefaultValues::test 0aec57fab35764525195e6c0e08a63ff10a467be684295381731c04e836539b5
// pending-parity-case: LibreMetaverse.Tests/AppearanceTests.cs::AppearanceTests.AvatarAppearanceEventArgs_DecodeVisualParams_MatchesAvatarDecodeVisualParams::test ff87dd3a506027ad248006b3b55d85a18b0810bc5b4e3a72eb81d31ebbe4184a
// pending-parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.Bake_GrayscaleLayerTexture_DoesNotThrow::test c3fc1d2c16b5f8d69678a6cf05a3a275e0b657f3a6e7f6788f350f2a107200b3
// pending-parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.Bake_GrayscaleHairTextureNoSkin_DoesNotThrow::test e48b737d9138e63cda98b5e80bf561a80d1dab5de78901ac9e1bb320ca281eb6
// pending-parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.LoadResourceLayer_HeadColorTga_LoadsFromCharacterDirectory::test 5a9b80005a51f04a227ca392218725d50704d038783e04fc49bb955997c22e76
// pending-parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.LoadResourceLayer_CachedResourceNotCorruptedByCallerMutation::test 18b458bbd7bb13d2b68711f7d3495193f1b4edac9c1f776930ad2c172f97cd49
// pending-parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.ResizeToBakeDimensions_SourceSmallerInOneAxisOnly_ScalesRatherThanTiles::test ce5bd346b48cb19dbc0248403eafefb07309d4e05e675e9a02db5a46d7cd65ff
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_ClothingWearable_ReturnsWearableTypeAndZeroLayer::test 898a14ff28d953a58a01c3a0e5d9d9fcbbe61376a33fdc5caf20268aaac76242
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_BodyPartShape_ReturnsEmpty::test 4f009136d265da91afb9eafd060faf459f6c510ca0502a33ce9e128d241f9bd1
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_AttachmentObject_ReturnsEmpty::test a838329a7a23081a4022671bc794e7e706c9ab7da51598280a8befbb824d336d
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_AllBodyPartTypes_ReturnEmpty::test aa057d55a3b34846567efe382b6e090b5d91935a4ffa6166093b34870ee5da23
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_SuccessResponse_ReturnsAllParsedLinks::test 416a1f023a2ae3b883c4c49f0a774c84a2f1a3562abb3ec848f86fe53d929f63
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_ServerError_ThrowsHttpRequestException::test 53e3956676e546ab8d69bb63a071028b29151013b6362c39f37516a36cad6641
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_NoCapability_ReturnsEmptyList::test d9cd50e620f69e40daab52d175886f3052d3741c5d39ef42549283ff6b2b7b44
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_EmptyList_InvokesCallbackWithTrueWithoutHttpCall::test e7ba88352761ca9fd3e956233e3743689b41f47bba09bfbb463c66f132273c70
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_SendsSingleBatchPost::test 369d99d37dca818991aca94d6ff9d7881874d74797c7a8b705c5925723e383db
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_PayloadContainsCorrectFieldsForEachLink::test b7629c3d061fe297e1c1c4f41ca4f8ce9cdeb2764525421be7bdc553a5f83063
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_CallbackTrueOnSuccess::test 22dc685f896ec0415e56c1c01846ad90a9757cf5d9a0ed0d3706a76ee325d92d
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_ServerError_CallbackFalse::test 5fd9b2967bc61933cea80e01c55ee809f6ade246f10fb4048ec2668e0cc71dbf
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCategoryLinks_UsesGetWithNoTidParameter::test 3079c03ab4b14d4ac10a410b3685f1593ea267c52de7b6323f9e8e8387314f29
// pending-parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCategory_UsesGetWithNoTidParameter::test 98cd0aa58ce9640263d07aecd45337113b07acfc76640d17c5812abe05d35c73
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.InventoryAISClient_Null_Throws::test ac92e375efc45c50b37e6c82aea372d605fceb470ce053a025e05ff47a5c7121
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.GridManager_Null_Throws::test e972a95f334b0f46948cda34c27124c28c44faf4372d0e598d7dc60050b49be4
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.InventoryManager_Null_Throws::test 5e7498066c944800354abbc71eea825fe90d0c40a25a6065967a3ebcc9efea74
@@ -80,47 +9,6 @@
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.ObjectManager_Null_Throws::test 8a11feba0edfb35e5dd99f681182e8f5a5cf8a3f738211b45a683a8a2ad45ddb
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AgentManager_Null_Throws::test d86e41b6cdd03063685aa330c31b9a274c1d400b9a461abf3065fe185fe0bbf0
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AvatarManager_Null_Throws::test 54f23cbf0ba87b0525c2914e78d75808e93fd1c460fc0bec4d15f09575b1431c
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Constructor_WithNullClient_ThrowsArgumentNullException::test 243079029ec81f92ffbc587007a2421389963f72c7843786c5b9a8634a7ddca3
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Dispose_CalledOnce_DoesNotThrow::test 1d0daac0bfa091bc3c3bffddc32a1511295dd7101f8ab5dfbf305b13b9ac7405
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.Dispose_CalledMultipleTimes_DoesNotThrow::test 124b41c342a295f02e9d5991d1dead0ff3e9a780f96424b88f75117ca206460b
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.MaxClothingLayers_Returns60::test e35e779b342fdb54bb7f5b2061f70c35ccc3f7f9ba87f48b2697f24c10f94397
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.COF_InitiallyNull::test 2dee4e58f3f53e50c5389ba0594ea0b063af55871eefae49e99bdd4b5c28e552
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.AddPolicy_WithValidPolicy_ReturnsAddedPolicy::test 747db5d674a6cb0bc54bcb9d32501d5bab49e0df5e5e39f3781f88d5100ed244
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.RemovePolicy_WithValidPolicy_DoesNotThrow::test 41f61b917da0410be01b346644a76bc9c800f78d1e1ba6ef805b14cea1b4d00d
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.RemovePolicy_WithNonAddedPolicy_DoesNotThrow::test 4add41578d3d5340618cb29ed6969b84c5c3a80e6da09ccb03bc6872a6b6689a
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.ResolveInventoryLink_WithNonLinkItem_ReturnsSameItem::test 7430a64883f3938afe7da1b126e4ee460cbf2485e2d51035273b4ca8fd150960
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.ResolveInventoryLink_WithLinkItemNotInStore_ReturnsNull::test 21e0dae8c038d2381eef736ad30d48fc0247e0a55b86e75de02982290a3a9bd0
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithNoNameValues_ReturnsZero::test c9e017a587c37b694484feda4fab81ef7f2bb098bbf8f96f3cefbb21f1a568d8
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithAttachItemID_ReturnsCorrectUUID::test 23e6be11056b93d75424c23030e1d2082545f686d12c0aa14f66991664e347e8
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetAttachmentItemID_WithoutAttachItemID_ReturnsZero::test 7ee12fc59c8b5e5cb074e18c2af48c0f1b1d7be0937db65a7056e29e114b691f
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.IsObjectDescendentOf_WithZeroParentId_ReturnsFalse::test a878c220510aacbf61ebfb192952e26d96343312fcf9648e57e270e29aaa9d5d
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.IsObjectDescendentOf_WithDirectParent_ReturnsTrue::test ac3af5edef5ee67058a91a990bfde38e616d00919b0cd8e0b5f7345f7d8dab6c
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.FetchParent_WithZeroParentUUID_ReturnsNull::test ee6cc0cc9a48ec07afb8bd7fa8e1c53c2db258be11e2848ee5752e41eb34e45a
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CurrentOutfitFolderTests.GetCurrentOutfitLinks_WithNullCOF_ReturnsEmptyList::test 5c0d475ccd4423bc099eb141f36061fdfaa589fd36d27c907d03a9d0be3f6f48
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_WithValidPolicy_ReturnsThis::test 31c1829eaa2b82b498cc1eaea131d2e34d1a0b5620be24f4da853c62c49807cc
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_WithNullPolicy_ThrowsArgumentNullException::test 6a111f0ad2e91c8566b5459939e3ecc76282cd033bb8d6483e1672c36891e597
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_MultiplePolicies_AllAreConsulted::test 1767ee3dee6b07e71f10b52613962e211fcf9906a45d12188f517cf751896f24
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_SamePolicyTwice_OnlyAddsOnce::test e3d1340780680bfc46267194ddb3c47e4ae24299cf532d92dd71d9770f1955fa
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithAddedPolicy_RemovesPolicy::test 9dd407dceeacdcc60fccb0d9869ad261d90acd74ae4257331e0cf8d4be2ee603
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithNonAddedPolicy_DoesNotThrow::test 9fa18bd628f6f02912168309e8e2afb6346a73a235dcc16cc81bc35e2dcf766f
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithNull_DoesNotThrow::test 28075d5f7c5458f1ccdf9981970e212e1663a9e5ebee65fce636a11a22390bfb
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithNoPolicies_ReturnsTrue::test 74bc06bb6cedfe84a3cd7e5b4c32b251ee6eff81c272c7d899436dbd7f21886b
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithNullItem_ThrowsArgumentNullException::test a47ebf9c89b603c10109c6e643de5043ba13cd2eaf5f04d73ab1fd5f40e9b332
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithAllPoliciesAllowing_ReturnsTrue::test 32c2f5dcc5cbdf24e4205c25c833aaa3c0f203aeb8ff20c30fdac1264f12f6bd
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithOnePolicyDenying_ReturnsFalse::test f38d910142a563bded6d35b969c0c560ffa072fd8df073a15fbbbed745681d0d
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithAllPoliciesDenying_ReturnsFalse::test 6cdda22d59a3cd690a187b597a7c8a2474e43c27902e737f15f0c71adc5bcf7c
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithNoPolicies_ReturnsTrue::test fe53b90f59fd9a73c4af1b7071f04cae0a7b623d6a50a3d8eb2fcae82e6872e9
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithNullItem_ThrowsArgumentNullException::test 8a39f9c6e26f3a136de641f38507db144f731aed0861edbeafcfdb70306ecffb
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithAllPoliciesAllowing_ReturnsTrue::test 2cbe1f0a5907c1dfa01af4329fa64177463798ec6caa357c8da8f5d3291901bf
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithOnePolicyDenying_ReturnsFalse::test 11896040dd8e4b5bbd7d85a1da126c648ad77eb349a73edf1e18fa5710cde1c9
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithAllPoliciesDenying_ReturnsFalse::test 62b8478f593f2bf16b44267d80668e327a245d0b5c0386e7229a5857d669ae6c
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNoPolicies_CompletesSuccessfully::test e3f6824512503bea532826b9a9e4f7bb5abdea53f962fbbc6fdae299a6fe44a0
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithMultiplePolicies_CallsAllPolicies::test 4797cc1851d78364553dac59a6f9a894107fa04fb3fddfb52ed0e4fb5b4bcd1a
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithCancelledToken_ThrowsOperationCanceledException::test e4eaf9c1ee2d55a3e6c7bca634ff1af2a0e19b7560ea616984d258df46ec89fd
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithEmptyLists_DoesNotThrow::test 6ba7e22fb7425e523f301612f7d7f6aed88e7d422050a70137d93b4a8004ae50
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite::test c512c54f78c46fb4774d9b5c9c1057627592e1c25267811cddf3d687d3d1ec4d
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow::test a73a01065867d90305f491e41d9ea61bc27b80c026672ed920368877bfb0fe14
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test 697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test 5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109
@@ -232,14 +120,6 @@
// pending-parity-case: LibreMetaverse.Tests/MarketplaceManagerTests.cs::MarketplaceManagerTests.MarketplaceErrorEventArgs_StoresMessageAndException::test cb0213208ab6b63f32a950f1771354fae4b6904c72720b1705ddf5b8355ffc7d
// pending-parity-case: LibreMetaverse.Tests/MarketplaceManagerTests.cs::MarketplaceManagerTests.MarketplaceErrorEventArgs_WithoutException_HasNullException::test de7c8addfb291cba5dee890a98ac8b5f27b192d0b75894c4102303a49785de47
// pending-parity-case: LibreMetaverse.Tests/MarketplaceManagerTests.cs::MarketplaceManagerTests.MarketplaceListing_DefaultValues_AreCorrect::test 70a29163581702fdc32c54a398ad4cefa608da8b1bc9d54a4e9e191e95398fb7
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.IsLink_TextureItemWithLinkAssetType_ReturnsTrue::test b85452466c0fdc47a44cc08166f93a660bbf466b0e2205910c1ea84fee22896c
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.IsLink_NormalTexture_ReturnsFalse::test 8c08505d6e89c4395115dbb060529419755aa1a81ca8889804ea00e6f94bf534
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.ResolvedItemID_OnMisclassifiedTextureLink_ReturnsAssetUUID::test cad5b5fb10523254ff19b7842b0e48984bc75182a26bc897f165a43770bb47f4
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Contains_FindsTargetByResolvedItemID::test 3c1f8596462c3a9e229c84ea44e44728bf175e1a0e8213ae92d1b02b08320b0b
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsWearable_WhenTargetIsWearable::test c7ce0b860743cfa5225de4322c8d7bf95094296b1dd2d07fd273bbd4e81a9fcf
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment::test f58debff8f66b7b417215eb6879f96935647b66cc619fdb00e6815c9ce5e7c67
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject::test b8137c994019103977b0b85d8c148e12ae8f9051334ca487db7c88bcbe707d40
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test 6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b
// pending-parity-case: LibreMetaverse.Tests/OfflineMessagesTests.cs::OfflineMessagesTests.RetrieveInstantMessagesAsync_BareArrayOfArraysResponse_RaisesInstantMessage::test d51f69e0fa1e1e59a5cedec7d60315d1f590848672a9f4f0c5e21d2abff5c372
// pending-parity-case: LibreMetaverse.Tests/OfflineMessagesTests.cs::OfflineMessagesTests.RetrieveInstantMessagesAsync_MessagesMapResponse_RaisesInstantMessage::test dbce6320d57d12ca5557cfba65257e15be314cde947b20dd21bc386819fcb3ce
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathBegin::test 5c8e14e1d08a91a4e7c3e252c932b8fe15cf6b5231cc9fe5bb7ef2ae6e2c8299

View File

@@ -0,0 +1,142 @@
// Exact Rust translations of LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs.
// Source SHA-256: f1e2a14f9671d6af084d72cc5280daf372985bf1b08c75e5989d9c8df0932a64
use libremetaverse::{
GridClient, Inventory, InventoryAttachment, InventoryObject, InventoryTexture,
InventoryWearable,
};
use libremetaverse_types::{AssetType, AttachmentPoint, UUID, WearableType};
fn store() -> Inventory {
Inventory::new_with_grid_client_uuid(GridClient::new().unwrap(), UUID::random().unwrap())
.unwrap()
}
fn texture_link(target: UUID) -> InventoryTexture {
let mut item = InventoryTexture::new(UUID::random().unwrap()).unwrap();
item.base.set_asset_type(AssetType::Link);
item.base.set_asset_uuid(target);
item
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.IsLink_TextureItemWithLinkAssetType_ReturnsTrue::test b85452466c0fdc47a44cc08166f93a660bbf466b0e2205910c1ea84fee22896c translated
#[test]
fn is_link_texture_item_with_link_asset_type_returns_true() {
let item = texture_link(UUID::random().unwrap());
assert!(item.base.is_link().unwrap());
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.IsLink_NormalTexture_ReturnsFalse::test 8c08505d6e89c4395115dbb060529419755aa1a81ca8889804ea00e6f94bf534 translated
#[test]
fn is_link_normal_texture_returns_false() {
let mut item = InventoryTexture::new(UUID::random().unwrap()).unwrap();
item.base.set_asset_type(AssetType::Texture);
item.base.set_asset_uuid(UUID::random().unwrap());
assert!(!item.base.is_link().unwrap());
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.ResolvedItemID_OnMisclassifiedTextureLink_ReturnsAssetUUID::test cad5b5fb10523254ff19b7842b0e48984bc75182a26bc897f165a43770bb47f4 translated
#[test]
fn resolved_item_id_on_misclassified_texture_link_returns_asset_uuid() {
let target = UUID::random().unwrap();
let item = texture_link(target);
assert_eq!(item.base.resolved_item_id(), target);
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Contains_FindsTargetByResolvedItemID::test 3c1f8596462c3a9e229c84ea44e44728bf175e1a0e8213ae92d1b02b08320b0b translated
#[test]
fn inventory_store_contains_finds_target_by_resolved_item_id() {
let store = store();
let target_id = UUID::random().unwrap();
let mut target = InventoryWearable::new(target_id).unwrap();
target.set_wearable_type(WearableType::Shirt);
target.base.set_asset_type(AssetType::Clothing);
target.base.set_asset_uuid(UUID::random().unwrap());
store.update_node_for(target.base.base).unwrap();
let link = texture_link(target_id);
assert!(
store
.contains_with_uuid(link.base.resolved_item_id())
.unwrap()
);
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsWearable_WhenTargetIsWearable::test c7ce0b860743cfa5225de4322c8d7bf95094296b1dd2d07fd273bbd4e81a9fcf translated
#[test]
fn inventory_store_indexer_returns_wearable_when_target_is_wearable() {
let store = store();
let target_id = UUID::random().unwrap();
let mut target = InventoryWearable::new(target_id).unwrap();
target.set_wearable_type(WearableType::Shirt);
target.base.set_asset_type(AssetType::Clothing);
target.base.set_asset_uuid(UUID::random().unwrap());
target.base.base.set_name("Test Shirt".into());
store.update_node_for(target.base.base).unwrap();
let link = texture_link(target_id);
let resolved = store
.get_value_or_default_with_uuid_a7c63fbe::<InventoryWearable>(link.base.resolved_item_id())
.unwrap();
assert!(
resolved.is_some(),
"target must retain its InventoryWearable type"
);
assert_eq!(resolved.unwrap().wearable_type(), WearableType::Shirt);
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment::test f58debff8f66b7b417215eb6879f96935647b66cc619fdb00e6815c9ce5e7c67 translated
#[test]
fn inventory_store_indexer_returns_attachment_when_target_is_attachment() {
let store = store();
let target_id = UUID::random().unwrap();
let mut target = InventoryAttachment::new(target_id).unwrap();
target.set_attachment_point(AttachmentPoint::Chest);
target.base.set_asset_type(AssetType::Object);
target.base.set_asset_uuid(UUID::random().unwrap());
target.base.base.set_name("Test Attachment".into());
store.update_node_for(target.base.base).unwrap();
let link = texture_link(target_id);
let resolved = store
.get_value_or_default_with_uuid_a7c63fbe::<InventoryAttachment>(
link.base.resolved_item_id(),
)
.unwrap();
assert!(
resolved.is_some(),
"target must retain its InventoryAttachment type"
);
assert_eq!(resolved.unwrap().attachment_point(), AttachmentPoint::Chest);
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject::test b8137c994019103977b0b85d8c148e12ae8f9051334ca487db7c88bcbe707d40 translated
#[test]
fn inventory_store_indexer_returns_object_when_target_is_object() {
let store = store();
let target_id = UUID::random().unwrap();
let mut target = InventoryObject::new(target_id).unwrap();
target.set_attach_point(AttachmentPoint::RightHand);
target.base.set_asset_type(AssetType::Object);
target.base.set_asset_uuid(UUID::random().unwrap());
target.base.base.set_name("Test Object".into());
store.update_node_for(target.base.base).unwrap();
let link = texture_link(target_id);
let resolved = store
.get_value_or_default_with_uuid_a7c63fbe::<InventoryObject>(link.base.resolved_item_id())
.unwrap();
assert!(
resolved.is_some(),
"target must retain its InventoryObject type"
);
assert_eq!(resolved.unwrap().attach_point(), AttachmentPoint::RightHand);
}
// parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test 6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b translated
#[test]
fn inventory_store_not_contains_when_target_absent() {
let store = store();
let link = texture_link(UUID::random().unwrap());
assert!(
!store
.contains_with_uuid(link.base.resolved_item_id())
.unwrap()
);
}

View File

@@ -9,7 +9,7 @@
use libremetaverse::http::DownloadManager;
use libremetaverse::packets::PacketType;
use libremetaverse::{GridClient, GridLayerType, HttpCapsClient, NetworkManager};
use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::{block_on, live_grid_credentials};
use libremetaverse_types::compat::{
CancellationToken, CancellationTokenSource, HttpMessageHandler, HttpRequest, HttpResponse, Uri,
};
@@ -158,35 +158,8 @@ where
.await
}
fn credential(name: &str) -> String {
if let Ok(value) = std::env::var(name)
&& !value.trim().is_empty()
{
return value;
}
let dotenv = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join(".env");
let contents = std::fs::read_to_string(dotenv).expect("read workspace .env");
contents
.lines()
.find_map(|line| {
let line = line.trim().strip_prefix("export ").unwrap_or(line.trim());
let (key, value) = line.split_once('=')?;
(key.trim() == name).then(|| value.trim().trim_matches(['\'', '"']).to_owned())
})
.filter(|value| !value.is_empty())
.unwrap_or_else(|| panic!("live test requires {name}"))
}
fn live_credentials() -> (String, String, String, String) {
let full_name = credential("GRID_USER");
let password = credential("GRID_PASSWORD");
let login_url = credential("GRID_LOGIN_URL");
let mut names = full_name.split(' ');
let first = names.next().expect("live first name").to_owned();
let last = names.next().expect("live last name").to_owned();
(first, last, password, login_url)
live_grid_credentials()
}
// parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully::test f8feb54d38f79624ddf1d368e0599259ff9d474c08ea0249a8f244460a1f533f translated