Files
MetaCrate/crates/libremetaverse/src/world_internal_semantics.rs
Chili Palmer c9a1170a27
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
Complete first release candidate audit (#107)
2026-08-12 14:44:28 +00:00

270 lines
10 KiB
Rust

// Exact Rust translations of the C# tests that deliberately invoke internal handlers.
// Source SHA-256: 74fa1275cd1f6c2a372af2fefc011a3e764f028a8957291d61f599bcb023a8ff
// Source SHA-256: 7b67b72c2ad652626126d0e4eda7e8c26fc94c28f0f915238b2925f80a3d4af2
#![allow(clippy::float_cmp)] // Wire fixtures use exactly representable values.
use super::*;
use libremetaverse_structured_data::{OSD, OSDMap};
use libremetaverse_types::{UUID, Vector3};
use packets::{
EstateOwnerMessagePacket, EstateOwnerMessagePacketAgentDataBlock,
EstateOwnerMessagePacketMethodDataBlock, EstateOwnerMessagePacketParamListBlock,
};
use std::collections::HashMap;
use std::future::Future;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Wake, Waker};
use std::time::Duration;
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
fn ids(values: impl IntoIterator<Item = UUID>) -> OSD {
OSD::Array(values.into_iter().map(OSD::UUID).collect())
}
fn map(entries: impl IntoIterator<Item = (&'static str, OSD)>) -> OSDMap {
OSDMap::new_with_dictionary(
entries
.into_iter()
.map(|(key, value)| (key.into(), value))
.collect(),
)
.expect("OSDMap dictionary constructor")
}
fn simulator() -> Simulator {
Simulator::new(
GridClient::new().expect("simulator GridClient constructor"),
"127.0.0.1:13".parse().unwrap(),
0,
None,
None,
)
.expect("Simulator constructor")
}
fn estate_packet(blocked: &[UUID], trusted: &[UUID], allowed: &[UUID]) -> EstateOwnerMessagePacket {
let counts = [
"1".to_owned(),
"0".to_owned(),
blocked.len().to_string(),
trusted.len().to_string(),
allowed.len().to_string(),
];
let mut parameters = counts
.into_iter()
.map(|value| EstateOwnerMessagePacketParamListBlock {
parameter: value.as_bytes().to_vec(),
})
.collect::<Vec<_>>();
parameters.extend(blocked.iter().chain(trusted).chain(allowed).map(|id| {
EstateOwnerMessagePacketParamListBlock {
parameter: id.get_bytes().expect("UUID bytes"),
}
}));
EstateOwnerMessagePacket {
agent_data: EstateOwnerMessagePacketAgentDataBlock {
agent_id: uuid(),
session_id: uuid(),
transaction_id: uuid(),
},
method_data: EstateOwnerMessagePacketMethodDataBlock {
method: b"setexperience".to_vec(),
invoice: UUID::zero(),
},
param_list: parameters,
}
}
fn block_on_with<F: Future>(future: F, mut after_first_poll: impl FnMut()) -> F::Output {
struct ThreadWake(std::thread::Thread);
impl Wake for ThreadWake {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
}
let waker = Waker::from(Arc::new(ThreadWake(std::thread::current())));
let mut context = Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
let mut invoked = false;
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => return value,
Poll::Pending if !invoked => {
invoked = true;
after_first_poll();
}
Poll::Pending => std::thread::park_timeout(Duration::from_millis(10)),
}
}
}
// parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_ParsesBlockedTrustedAndAllowedLists::test 49a77aa9d7c5bf1232082a08594e7ebcfafad11ab110c9e15d2ad4ea1831c649 translated
#[test]
fn set_experience_parses_blocked_trusted_and_allowed_lists() {
let blocked = uuid();
let trusted = uuid();
let allowed = [uuid(), uuid()];
let client = GridClient::new().expect("GridClient constructor");
let estate = client.estate();
let received = Arc::new(Mutex::new(None));
let captured = Arc::clone(&received);
let _subscription = estate.subscribe_estate_experience_reply(Arc::new(move |reply| {
*captured.lock().unwrap() = Some((reply.blocked(), reply.trusted(), reply.allowed()));
}));
estate
.estate_owner_message_handler(estate_packet(&[blocked], &[trusted], &allowed), simulator())
.unwrap();
let reply = received.lock().unwrap();
let (actual_blocked, actual_trusted, actual_allowed) =
reply.as_ref().expect("EstateExperienceReply event");
assert_eq!(actual_blocked, &[blocked]);
assert_eq!(actual_trusted, &[trusted]);
assert_eq!(actual_allowed, &allowed);
}
// parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_AllEmptyLists_ProducesEmptyResult::test 711eaebb11f5d7ece92946d3de240a341a4b1fd8f30617fd97cb059d0dc6a0b9 translated
#[test]
fn set_experience_all_empty_lists_produces_empty_result() {
let client = GridClient::new().expect("GridClient constructor");
let estate = client.estate();
let received = Arc::new(Mutex::new(None));
let captured = Arc::clone(&received);
let _subscription = estate.subscribe_estate_experience_reply(Arc::new(move |reply| {
*captured.lock().unwrap() = Some((reply.blocked(), reply.trusted(), reply.allowed()));
}));
estate
.estate_owner_message_handler(estate_packet(&[], &[], &[]), simulator())
.unwrap();
let reply = received.lock().unwrap();
let (blocked, trusted, allowed) = reply.as_ref().expect("EstateExperienceReply event");
assert!(blocked.is_empty());
assert!(trusted.is_empty());
assert!(allowed.is_empty());
}
#[test]
fn land_stat_caps_reply_preserves_mono_score_and_emits_after_decode() {
let client = GridClient::new().expect("GridClient constructor");
let estate = client.estate();
let task_id = uuid();
let received = Arc::new(Mutex::new(None));
let captured = Arc::clone(&received);
let _subscription = estate.subscribe_top_scripts_reply(Arc::new(move |reply| {
*captured.lock().unwrap() = Some((reply.object_count(), reply.tasks()));
}));
let mut message =
messages::linden::LandStatReplyMessage::new().expect("LandStatReplyMessage constructor");
message.report_type = EstateToolsLandStatReportType::TopScripts as u32;
message.total_object_count = 1;
message.report_data_blocks = vec![messages::linden::LandStatReplyMessageReportDataBlock {
location: Vector3 {
x: 1.0,
y: 2.0,
z: 3.0,
},
mono_score: 4.5,
owner_name: "Owner Resident".into(),
score: 9.25,
task_id,
task_local_id: 17,
task_name: "Scripted object".into(),
time_stamp: std::time::SystemTime::UNIX_EPOCH,
}];
let encoded = message.serialize().expect("serialize LandStatReply");
let mut decoded =
messages::linden::LandStatReplyMessage::new().expect("LandStatReplyMessage constructor");
decoded
.deserialize(encoded)
.expect("deserialize LandStatReply");
client
.network()
.dispatch_caps_event("LandStatReply", &message, simulator());
let received = received.lock().unwrap();
let (count, tasks) = received.as_ref().expect("TopScriptsReply event");
assert_eq!(*count, 1);
let task = tasks.get(&task_id).expect("reported task");
assert_eq!(task.mono_score, 4.5);
assert_eq!(task.score, 9.25);
assert_eq!(task.task_local_id, 17);
assert_eq!(task.task_name, "Scripted object");
assert_eq!(task.owner_name, "Owner Resident");
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Allowed_ReturnsAllow::test cf095ab428713004ebac6e1915a47ae0c03bd2958addbba2a39fd2f0edf51a74 translated
#[test]
fn extract_experience_permission_allowed_returns_allow() {
let id = uuid();
assert_eq!(
AgentManager::extract_experience_permission(map([("experiences", ids([id]))]), id).unwrap(),
"Allow"
);
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Blocked_ReturnsBlock::test af084d1ab7a11079e6251865e8fd5aecfde38d2df359ebc1e208bc74925935c0 translated
#[test]
fn extract_experience_permission_blocked_returns_block() {
let id = uuid();
assert_eq!(
AgentManager::extract_experience_permission(map([("blocked", ids([id]))]), id).unwrap(),
"Block"
);
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_NeitherList_ReturnsForget::test 93204469ecfec2f3f4eb6838c99f6e2baf09bf76a075958d1ed369ba03767158 translated
#[test]
fn extract_experience_permission_neither_list_returns_forget() {
let id = uuid();
let input = OSDMap::new_with_dictionary(HashMap::from([
("experiences".into(), ids([uuid()])),
("blocked".into(), ids([uuid()])),
]))
.expect("OSDMap dictionary constructor");
assert_eq!(
AgentManager::extract_experience_permission(input, id).unwrap(),
"Forget"
);
}
// parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_IgnoresPostBody_UsesSimConsoleResponseEvent::test df6eb48241f71c34074be66869c9ae1d5980186583259568728261b5eedd7c48 translated
#[test]
fn send_sim_console_command_ignores_post_body_and_uses_response_event() {
let client = GridClient::new().expect("GridClient constructor");
let estate = client.estate();
let network = client.network();
let result = block_on_with(
estate.send_sim_console_command("show name".into(), Some(Duration::from_secs(5)), None),
|| {
let mut message = messages::linden::SimConsoleResponseMessage::new()
.expect("SimConsoleResponseMessage constructor");
message.body = "Agent Name".into();
network
.raise_sim_console_response(message, simulator())
.unwrap();
},
)
.unwrap();
assert_eq!(result.as_deref(), Some("Agent Name"));
}
// parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_NoResponseEvent_ReturnsNullAfterTimeout::test 2ac5a6c96f3acec341056387d727288fd3661565bd2a7c385078602efe8661da translated
#[test]
fn send_sim_console_command_no_response_event_returns_none_after_timeout() {
let client = GridClient::new().expect("GridClient constructor");
let result = block_on_with(
client.estate().send_sim_console_command(
"show name".into(),
Some(Duration::from_millis(200)),
None,
),
|| {},
)
.unwrap();
assert!(result.is_none());
}