// 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 { 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) -> 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 = 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 = 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 = 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); } }