Translate HTTP and network tests

This commit is contained in:
2026-08-08 18:30:51 +02:00
parent de6e611ef0
commit 6d4d9adaf4
13 changed files with 1793 additions and 1051 deletions

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
# Live-grid compatibility tests run automatically when all three values are set.
GRID_USER=First Last
GRID_PASSWORD=
GRID_LOGIN_URL=https://grid.example.test/login

View File

@@ -334,8 +334,8 @@ markers in hand-written Rust files, so regeneration records unresolved cases
without creating fake executable tests and fails on body drift. The checked-in without creating fake executable tests and fails on body drift. The checked-in
audit reports pending, translated, ignored-live, benchmark, drifted, missing, audit reports pending, translated, ignored-live, benchmark, drifted, missing,
duplicate, stale, and unreviewed cases. The initial handover contained 1,289 duplicate, stale, and unreviewed cases. The initial handover contained 1,289
unreviewed cases; the current ledger contains 145 translated, four unreviewed cases; the current ledger contains 149 translated, three reviewed
benchmark-reviewed, and 1,140 pending cases. live-grid cases, four benchmark-reviewed, and 1,133 pending cases.
The Rust tests must call the public APIs rather than internal replacements. The Rust tests must call the public APIs rather than internal replacements.
Where the C# tests call internal members through friend-assembly access, record Where the C# tests call internal members through friend-assembly access, record
@@ -358,8 +358,8 @@ The test-suite gate is complete only when:
- a controlled test run fails at standardized unimplemented member IDs rather - a controlled test run fails at standardized unimplemented member IDs rather
than from missing symbols, compile errors, malformed fixtures, or weakened than from missing symbols, compile errors, malformed fixtures, or weakened
assertions; assertions;
- live-server/device tests are faithfully translated but `#[ignore]` by default - live-server/device tests are faithfully translated and are conditionally
with documented opt-in configuration; ignored only while their documented credentials are absent;
- no production method contains real behavior beyond what is required to make - no production method contains real behavior beyond what is required to make
signatures and constants compile. signatures and constants compile.
@@ -712,8 +712,10 @@ Test translation rules:
- async tests use paused/injected time where possible; no arbitrary sleeps. - async tests use paused/injected time where possible; no arbitrary sleeps.
- Moq interaction tests prefer a small fake recording calls; use Mockall only - Moq interaction tests prefer a small fake recording calls; use Mockall only
when a trait contract has many independent expectations. when a trait contract has many independent expectations.
- `RequiresLiveServer` tests become `#[ignore]` and require explicit grid URL, - `RequiresLiveServer` tests use `GRID_USER`, `GRID_PASSWORD`, and
first/last name, and secret environment variables. They never run in normal CI. `GRID_LOGIN_URL` from the process environment or the ignored workspace
`.env`; they are ignored when any value is absent and run automatically when
all three are present. `.env.example` documents the non-secret shape.
- benchmark-category methods move to Criterion and are removed from correctness - benchmark-category methods move to Criterion and are removed from correctness
pass/fail counts. pass/fail counts.
- malformed wire/asset tests become fuzz seeds after direct parity is passing. - malformed wire/asset tests become fuzz seeds after direct parity is passing.

View File

@@ -26249,8 +26249,8 @@ P:LibreMetaverse.GridClient.Marketplace property LibreMetaverse.Marketplace.Mark
P:LibreMetaverse.GridClient.Network property LibreMetaverse.NetworkManager LibreMetaverse.GridClient.Network libremetaverse libremetaverse::GridClient::network pub fn network(&self) -> libremetaverse::NetworkManager ; pub fn set_network(&mut self, value: libremetaverse::NetworkManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Network property LibreMetaverse.NetworkManager LibreMetaverse.GridClient.Network libremetaverse libremetaverse::GridClient::network pub fn network(&self) -> libremetaverse::NetworkManager ; pub fn set_network(&mut self, value: libremetaverse::NetworkManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Objects property LibreMetaverse.ObjectManager LibreMetaverse.GridClient.Objects libremetaverse libremetaverse::GridClient::objects pub fn objects(&self) -> libremetaverse::ObjectManager ; pub fn set_objects(&mut self, value: libremetaverse::ObjectManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Objects property LibreMetaverse.ObjectManager LibreMetaverse.GridClient.Objects libremetaverse libremetaverse::GridClient::objects pub fn objects(&self) -> libremetaverse::ObjectManager ; pub fn set_objects(&mut self, value: libremetaverse::ObjectManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Parcels property LibreMetaverse.ParcelManager LibreMetaverse.GridClient.Parcels libremetaverse libremetaverse::GridClient::parcels pub fn parcels(&self) -> libremetaverse::ParcelManager ; pub fn set_parcels(&mut self, value: libremetaverse::ParcelManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Parcels property LibreMetaverse.ParcelManager LibreMetaverse.GridClient.Parcels libremetaverse libremetaverse::GridClient::parcels pub fn parcels(&self) -> libremetaverse::ParcelManager ; pub fn set_parcels(&mut self, value: libremetaverse::ParcelManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Self property LibreMetaverse.AgentManager LibreMetaverse.GridClient.Self libremetaverse libremetaverse::GridClient::self_ pub fn self_(&self) -> libremetaverse::AgentManager ; pub fn set_self_(&mut self, value: libremetaverse::AgentManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Self property LibreMetaverse.AgentManager LibreMetaverse.GridClient.Self libremetaverse libremetaverse::GridClient::self_ pub fn self_(&mut self) -> &mut libremetaverse::AgentManager mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Settings property LibreMetaverse.Settings LibreMetaverse.GridClient.Settings libremetaverse libremetaverse::GridClient::settings pub fn settings(&self) -> libremetaverse::Settings ; pub fn set_settings(&mut self, value: libremetaverse::Settings) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Settings property LibreMetaverse.Settings LibreMetaverse.GridClient.Settings libremetaverse libremetaverse::GridClient::settings pub fn settings(&mut self) -> &mut libremetaverse::Settings mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Sound property LibreMetaverse.SoundManager LibreMetaverse.GridClient.Sound libremetaverse libremetaverse::GridClient::sound pub fn sound(&self) -> libremetaverse::SoundManager ; pub fn set_sound(&mut self, value: libremetaverse::SoundManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Sound property LibreMetaverse.SoundManager LibreMetaverse.GridClient.Sound libremetaverse libremetaverse::GridClient::sound pub fn sound(&self) -> libremetaverse::SoundManager ; pub fn set_sound(&mut self, value: libremetaverse::SoundManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Stats property LibreMetaverse.Stats.UtilizationStatistics LibreMetaverse.GridClient.Stats libremetaverse libremetaverse::GridClient::stats pub fn stats(&self) -> libremetaverse::stats::UtilizationStatistics ; pub fn set_stats(&mut self, value: libremetaverse::stats::UtilizationStatistics) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Stats property LibreMetaverse.Stats.UtilizationStatistics LibreMetaverse.GridClient.Stats libremetaverse libremetaverse::GridClient::stats pub fn stats(&self) -> libremetaverse::stats::UtilizationStatistics ; pub fn set_stats(&mut self, value: libremetaverse::stats::UtilizationStatistics) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.GridClient.Terrain property LibreMetaverse.TerrainManager LibreMetaverse.GridClient.Terrain libremetaverse libremetaverse::GridClient::terrain pub fn terrain(&self) -> libremetaverse::TerrainManager ; pub fn set_terrain(&mut self, value: libremetaverse::TerrainManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.GridClient.Terrain property LibreMetaverse.TerrainManager LibreMetaverse.GridClient.Terrain libremetaverse libremetaverse::GridClient::terrain pub fn terrain(&self) -> libremetaverse::TerrainManager ; pub fn set_terrain(&mut self, value: libremetaverse::TerrainManager) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
@@ -30566,7 +30566,7 @@ P:LibreMetaverse.Settings.Logging property LibreMetaverse.LoggingSettings LibreM
P:LibreMetaverse.Settings.Packets property LibreMetaverse.PacketSettings LibreMetaverse.Settings.Packets libremetaverse libremetaverse::Settings::packets pub fn packets(&self) -> libremetaverse::PacketSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.Packets property LibreMetaverse.PacketSettings LibreMetaverse.Settings.Packets libremetaverse libremetaverse::Settings::packets pub fn packets(&self) -> libremetaverse::PacketSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.Settings.Parcel property LibreMetaverse.ParcelSettings LibreMetaverse.Settings.Parcel libremetaverse libremetaverse::Settings::parcel pub fn parcel(&self) -> libremetaverse::ParcelSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.Parcel property LibreMetaverse.ParcelSettings LibreMetaverse.Settings.Parcel libremetaverse libremetaverse::Settings::parcel pub fn parcel(&self) -> libremetaverse::ParcelSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.Settings.TexturePipeline property LibreMetaverse.TexturePipelineSettings LibreMetaverse.Settings.TexturePipeline libremetaverse libremetaverse::Settings::texture_pipeline pub fn texture_pipeline(&self) -> libremetaverse::TexturePipelineSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.TexturePipeline property LibreMetaverse.TexturePipelineSettings LibreMetaverse.Settings.TexturePipeline libremetaverse libremetaverse::Settings::texture_pipeline pub fn texture_pipeline(&self) -> libremetaverse::TexturePipelineSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.Settings.Timing property LibreMetaverse.TimingSettings LibreMetaverse.Settings.Timing libremetaverse libremetaverse::Settings::timing pub fn timing(&self) -> libremetaverse::TimingSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.Timing property LibreMetaverse.TimingSettings LibreMetaverse.Settings.Timing libremetaverse libremetaverse::Settings::timing pub fn timing(&mut self) -> &mut libremetaverse::TimingSettings mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.Settings.UploadCost property System.Int32 LibreMetaverse.Settings.UploadCost libremetaverse libremetaverse::Settings::upload_cost pub fn upload_cost(&self) -> i32 ; pub fn set_upload_cost(&mut self, value: i32) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.UploadCost property System.Int32 LibreMetaverse.Settings.UploadCost libremetaverse libremetaverse::Settings::upload_cost pub fn upload_cost(&self) -> i32 ; pub fn set_upload_cost(&mut self, value: i32) shared_or_mutable_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.Settings.World property LibreMetaverse.WorldSettings LibreMetaverse.Settings.World libremetaverse libremetaverse::Settings::world pub fn world(&self) -> libremetaverse::WorldSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.Settings.World property LibreMetaverse.WorldSettings LibreMetaverse.Settings.World libremetaverse libremetaverse::Settings::world pub fn world(&self) -> libremetaverse::WorldSettings shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped
P:LibreMetaverse.SimChangedEventArgs.PreviousSimulator property LibreMetaverse.Simulator LibreMetaverse.SimChangedEventArgs.PreviousSimulator libremetaverse libremetaverse::SimChangedEventArgs::previous_simulator pub fn previous_simulator(&self) -> Option<libremetaverse::Simulator> shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.SimChangedEventArgs.PreviousSimulator property LibreMetaverse.Simulator LibreMetaverse.SimChangedEventArgs.PreviousSimulator libremetaverse libremetaverse::SimChangedEventArgs::previous_simulator pub fn previous_simulator(&self) -> Option<libremetaverse::Simulator> shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped
Can't render this file because it is too large.

View File

@@ -4,7 +4,10 @@
//! surfaces. Their behavior is implemented only when the owning API slice is //! surfaces. Their behavior is implemented only when the owning API slice is
//! ported. //! ported.
use std::collections::BTreeMap;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::{ use std::sync::{
Arc, Arc,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
@@ -91,7 +94,7 @@ impl CancellationToken {
} }
} }
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[derive(Debug, Default, Eq, Hash, PartialEq)]
pub struct Subscription; pub struct Subscription;
pub type EventHandler<T> = Arc<dyn Fn(T) + Send + Sync>; pub type EventHandler<T> = Arc<dyn Fn(T) + Send + Sync>;
@@ -143,17 +146,65 @@ pub struct TimeProvider;
pub struct MediaTypeHeaderValue(pub String); pub struct MediaTypeHeaderValue(pub String);
pub struct HttpMessageHandler; #[derive(Clone, Debug, Eq, PartialEq)]
pub struct HttpRequest {
pub method: String,
pub uri: Uri,
pub headers: BTreeMap<String, String>,
pub content_type: Option<String>,
pub body: Vec<u8>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HttpResponse {
pub status_code: u16,
pub headers: BTreeMap<String, String>,
pub content_type: Option<String>,
pub body: Vec<u8>,
}
impl HttpResponse {
#[must_use]
pub const fn is_success_status_code(&self) -> bool {
self.status_code >= 200 && self.status_code < 300
}
}
pub type HttpResponseFuture = Pin<Box<dyn Future<Output = HttpResponse> + Send>>;
type HttpHandler = dyn Fn(HttpRequest, CancellationToken) -> HttpResponseFuture + Send + Sync;
#[derive(Clone)]
pub struct HttpMessageHandler(Arc<HttpHandler>);
impl HttpMessageHandler {
pub fn new<F, Fut>(handler: F) -> Self
where
F: Fn(HttpRequest, CancellationToken) -> Fut + Send + Sync + 'static,
Fut: Future<Output = HttpResponse> + Send + 'static,
{
Self(Arc::new(move |request, cancellation_token| {
Box::pin(handler(request, cancellation_token))
}))
}
#[must_use]
pub fn send(
&self,
request: HttpRequest,
cancellation_token: CancellationToken,
) -> HttpResponseFuture {
(self.0)(request, cancellation_token)
}
}
pub struct HttpClient; pub struct HttpClient;
pub struct HttpResponse;
pub struct HttpMethod(pub String); pub struct HttpMethod(pub String);
pub struct AuthenticationHeaderValue(pub String); pub struct AuthenticationHeaderValue(pub String);
pub trait IProgress<T> {} pub trait IProgress<T>: Send + Sync {}
pub struct IEnumerator; pub struct IEnumerator;
@@ -200,3 +251,55 @@ pub struct UnicodeCategory(pub i32);
pub struct TextEncoding; pub struct TextEncoding;
pub struct SocketException; pub struct SocketException;
#[cfg(test)]
mod tests {
use super::{CancellationToken, HttpMessageHandler, HttpRequest, HttpResponse, Uri};
use std::collections::BTreeMap;
use std::future::Future;
use std::task::{Context, Poll, Waker};
#[test]
fn http_handler_preserves_request_and_response_data() {
let handler = HttpMessageHandler::new(|request, _cancellation_token| async move {
HttpResponse {
status_code: 201,
headers: request.headers,
content_type: request.content_type,
body: request.body,
}
});
let mut response = handler.send(
HttpRequest {
method: "PUT".into(),
uri: Uri("http://example.test/".into()),
headers: BTreeMap::from([("Location".into(), "slcaps://fixture".into())]),
content_type: Some("application/llsd+xml".into()),
body: b"test".to_vec(),
},
CancellationToken::default(),
);
let mut context = Context::from_waker(Waker::noop());
let response = match Future::poll(response.as_mut(), &mut context) {
Poll::Ready(response) => response,
Poll::Pending => panic!("immediate handler unexpectedly remained pending"),
};
assert!(response.is_success_status_code());
assert_eq!(response.headers["Location"], "slcaps://fixture");
assert_eq!(
response.content_type.as_deref(),
Some("application/llsd+xml")
);
assert_eq!(response.body, b"test");
assert!(
!HttpResponse {
status_code: 399,
headers: BTreeMap::new(),
content_type: None,
body: Vec::new(),
}
.is_success_status_code()
);
}
}

View File

@@ -53,6 +53,8 @@ pub enum Error {
Argument, Argument,
/// An index or destination buffer boundary was exceeded. /// An index or destination buffer boundary was exceeded.
IndexOutOfRange, IndexOutOfRange,
/// An operation observed a requested cancellation.
Cancelled,
} }
impl Error { impl Error {
@@ -61,7 +63,7 @@ impl Error {
pub const fn csharp_member(self) -> Option<&'static str> { pub const fn csharp_member(self) -> Option<&'static str> {
match self { match self {
Self::NotImplemented(error) => Some(error.csharp_member()), Self::NotImplemented(error) => Some(error.csharp_member()),
Self::ArgumentNull | Self::Argument | Self::IndexOutOfRange => None, Self::ArgumentNull | Self::Argument | Self::IndexOutOfRange | Self::Cancelled => None,
} }
} }
} }
@@ -79,6 +81,7 @@ impl fmt::Display for Error {
Self::ArgumentNull => formatter.write_str("required argument was null"), Self::ArgumentNull => formatter.write_str("required argument was null"),
Self::Argument => formatter.write_str("argument was invalid"), Self::Argument => formatter.write_str("argument was invalid"),
Self::IndexOutOfRange => formatter.write_str("index was out of range"), Self::IndexOutOfRange => formatter.write_str("index was out of range"),
Self::Cancelled => formatter.write_str("operation was cancelled"),
} }
} }
} }

View File

@@ -11377,19 +11377,11 @@ impl GridClient {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Parcels") libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Parcels")
} }
/// C# member: `P:LibreMetaverse.GridClient.Self`. /// C# member: `P:LibreMetaverse.GridClient.Self`.
pub fn self_(&self) -> libremetaverse::AgentManager { pub fn self_(&mut self) -> &mut libremetaverse::AgentManager {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Self")
}
/// Setter for C# member: `P:LibreMetaverse.GridClient.Self`.
pub fn set_self_(&mut self, value: libremetaverse::AgentManager) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Self") libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Self")
} }
/// C# member: `P:LibreMetaverse.GridClient.Settings`. /// C# member: `P:LibreMetaverse.GridClient.Settings`.
pub fn settings(&self) -> libremetaverse::Settings { pub fn settings(&mut self) -> &mut libremetaverse::Settings {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Settings")
}
/// Setter for C# member: `P:LibreMetaverse.GridClient.Settings`.
pub fn set_settings(&mut self, value: libremetaverse::Settings) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Settings") libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Settings")
} }
/// C# member: `P:LibreMetaverse.GridClient.Sound`. /// C# member: `P:LibreMetaverse.GridClient.Sound`.
@@ -25747,7 +25739,7 @@ impl Settings {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Settings.TexturePipeline") libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Settings.TexturePipeline")
} }
/// C# member: `P:LibreMetaverse.Settings.Timing`. /// C# member: `P:LibreMetaverse.Settings.Timing`.
pub fn timing(&self) -> libremetaverse::TimingSettings { pub fn timing(&mut self) -> &mut libremetaverse::TimingSettings {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Settings.Timing") libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Settings.Timing")
} }
/// C# member: `P:LibreMetaverse.Settings.UploadCost`. /// C# member: `P:LibreMetaverse.Settings.UploadCost`.

View File

@@ -5,9 +5,9 @@ Generated from LibreMetaverse `2aa70bb68513b39795da5d13c88f31b86e85a3ba`. Stable
## Status ## Status
- Total: **1,289** - Total: **1,289**
- Pending/unreviewed: **1,140** - Pending/unreviewed: **1,133**
- Translated/reviewed: **145** - Translated/reviewed: **149**
- Ignored live/reviewed: **0** (19 upstream live candidates) - Ignored live/reviewed: **3** (19 upstream live candidates)
- Benchmarks/reviewed: **4** (4 upstream benchmark candidates) - Benchmarks/reviewed: **4** (4 upstream benchmark candidates)
- Drifted: **0** - Drifted: **0**
@@ -214,9 +214,9 @@ Reviewed Rust tests live outside `generated_parity.rs` and carry a `parity-case`
| `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite::test` | `CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:588` | `` | `` | `pending source translation` | `pending` | `c512c54f78c46fb4774d9b5c9c1057627592e1c25267811cddf3d687d3d1ec4d` | | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite::test` | `CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:588` | `` | `` | `pending source translation` | `pending` | `c512c54f78c46fb4774d9b5c9c1057627592e1c25267811cddf3d687d3d1ec4d` |
| `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow::test` | `CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:601` | `` | `` | `pending source translation` | `pending` | `a73a01065867d90305f491e41d9ea61bc27b80c026672ed920368877bfb0fe14` | | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow::test` | `CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:601` | `` | `` | `pending source translation` | `pending` | `a73a01065867d90305f491e41d9ea61bc27b80c026672ed920368877bfb0fe14` |
| `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test` | `CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:627` | `` | `` | `pending source translation` | `pending` | `697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7` | | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test` | `CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow` | `` | `LibreMetaverse.Tests/CurrentOutfitFolderTests.cs:627` | `` | `` | `pending source translation` | `pending` | `697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7` |
| `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully::test` | `DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:46` | `` | `` | `pending source translation` | `pending` | `f8feb54d38f79624ddf1d368e0599259ff9d474c08ea0249a8f244460a1f533f` | | `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully::test` | `DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:46` | `` | `` | `tests/compat/tests/network_semantics.rs:192 (`queue_download_single_completes_successfully`)` | `translated` | `f8feb54d38f79624ddf1d368e0599259ff9d474c08ea0249a8f244460a1f533f` |
| `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall::test` | `DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:70` | `` | `` | `pending source translation` | `pending` | `dc3ffee1f6171833a8364750cfac2c6d3bfa812b404129e0425066ea212f2337` | | `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall::test` | `DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:70` | `` | `` | `tests/compat/tests/network_semantics.rs:229 (`queue_download_deduplicates_same_uri`)` | `translated` | `dc3ffee1f6171833a8364750cfac2c6d3bfa812b404129e0425066ea212f2337` |
| `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask::test` | `DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:101` | `` | `` | `pending source translation` | `pending` | `9eb3791b1def609d6f45be0fff8ae67ea04332045c0c419c6c2e7d118aeb6271` | | `LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask::test` | `DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask` | `` | `LibreMetaverse.Tests/DownloadManagerTests.cs:101` | `` | `` | `tests/compat/tests/network_semantics.rs:275 (`queue_download_cancellation_propagates`)` | `translated` | `9eb3791b1def609d6f45be0fff8ae67ea04332045c0c419c6c2e7d118aeb6271` |
| `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test` | `EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:105` | `Environment` | `` | `pending source translation` | `pending` | `5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4` | | `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test` | `EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:105` | `Environment` | `` | `pending source translation` | `pending` | `5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4` |
| `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test` | `EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:128` | `Environment` | `` | `pending source translation` | `pending` | `f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f` | | `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test` | `EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:128` | `Environment` | `` | `pending source translation` | `pending` | `f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f` |
| `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test` | `EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:158` | `Environment` | `` | `pending source translation` | `pending` | `bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109` | | `LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test` | `EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely` | `` | `LibreMetaverse.Tests/EnvironmentManagerTests.cs:158` | `Environment` | `` | `pending source translation` | `pending` | `bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109` |
@@ -258,7 +258,7 @@ Reviewed Rust tests live outside `generated_parity.rs` and carry a `parity-case`
| `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.Load_AutoDetectsGlbVsJson::test` | `GltfDocumentTests.Load_AutoDetectsGlbVsJson` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:252` | `` | `` | `pending source translation` | `pending` | `6d07930be2302e504ef9ece3fef10f47b855d33ae6c8bff152e82df0beb2a474` | | `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.Load_AutoDetectsGlbVsJson::test` | `GltfDocumentTests.Load_AutoDetectsGlbVsJson` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:252` | `` | `` | `pending source translation` | `pending` | `6d07930be2302e504ef9ece3fef10f47b855d33ae6c8bff152e82df0beb2a474` |
| `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_TrsDefaults::test` | `GltfDocumentTests.ParseNode_TrsDefaults` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:268` | `` | `` | `pending source translation` | `pending` | `0f0fbe116d20276c2aee917b940bded9de277ed0b3e3b659844c5b3532755259` | | `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_TrsDefaults::test` | `GltfDocumentTests.ParseNode_TrsDefaults` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:268` | `` | `` | `pending source translation` | `pending` | `0f0fbe116d20276c2aee917b940bded9de277ed0b3e3b659844c5b3532755259` |
| `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_ExplicitMatrix::test` | `GltfDocumentTests.ParseNode_ExplicitMatrix` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:280` | `` | `` | `pending source translation` | `pending` | `197ba2ee3a66cbb7d927acea13ce9dc84e62af8a500d939957749039118755c8` | | `LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_ExplicitMatrix::test` | `GltfDocumentTests.ParseNode_ExplicitMatrix` | `` | `LibreMetaverse.Tests/GltfDocumentTests.cs:280` | `` | `` | `pending source translation` | `pending` | `197ba2ee3a66cbb7d927acea13ce9dc84e62af8a500d939957749039118755c8` |
| `LibreMetaverse.Tests/GridClientTests.cs::GridClientTests.GetGridRegion::test` | `GridClientTests.GetGridRegion` | `` | `LibreMetaverse.Tests/GridClientTests.cs:90` | `GridClient, RequiresLiveServer` | `` | `pending source translation` | `pending` | `b0b72fc63fe829984167a479b763784c7daae8691399f3f3d2feb824090ad59f` | | `LibreMetaverse.Tests/GridClientTests.cs::GridClientTests.GetGridRegion::test` | `GridClientTests.GetGridRegion` | `` | `LibreMetaverse.Tests/GridClientTests.cs:90` | `GridClient, RequiresLiveServer` | `` | `tests/compat/tests/network_semantics.rs:372 (`get_grid_region_live`)` | `ignored-live` | `b0b72fc63fe829984167a479b763784c7daae8691399f3f3d2feb824090ad59f` |
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_ValidInput_DecodesCorrectly::test` | `HelpersZeroCodingTests.ZeroDecode_ValidInput_DecodesCorrectly` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:36` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:79 (`zero_decode_valid_input_decodes_correctly`)` | `translated` | `62353da2b1afb7bfdf5142706d15d22086a5e1e8c55f39af3e64686dda4ea071` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_ValidInput_DecodesCorrectly::test` | `HelpersZeroCodingTests.ZeroDecode_ValidInput_DecodesCorrectly` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:36` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:79 (`zero_decode_valid_input_decodes_correctly`)` | `translated` | `62353da2b1afb7bfdf5142706d15d22086a5e1e8c55f39af3e64686dda4ea071` |
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_NullSource_ThrowsArgumentNullException::test` | `HelpersZeroCodingTests.ZeroDecode_NullSource_ThrowsArgumentNullException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:64` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:97 (`zero_decode_null_source_throws_argument_null`)` | `translated` | `50ebbd8ea35b87df18cccdea9a26534d5c142228e5b1698cf191b9fd2e9a9957` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_NullSource_ThrowsArgumentNullException::test` | `HelpersZeroCodingTests.ZeroDecode_NullSource_ThrowsArgumentNullException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:64` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:97 (`zero_decode_null_source_throws_argument_null`)` | `translated` | `50ebbd8ea35b87df18cccdea9a26534d5c142228e5b1698cf191b9fd2e9a9957` |
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_NullDestination_ThrowsArgumentNullException::test` | `HelpersZeroCodingTests.ZeroDecode_NullDestination_ThrowsArgumentNullException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:73` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:108 (`zero_decode_null_destination_throws_argument_null`)` | `translated` | `97197cffb02908653d831f3a62cdce2c1ddd52818343e9aacebc9b4c5ac1ee3e` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroDecode_NullDestination_ThrowsArgumentNullException::test` | `HelpersZeroCodingTests.ZeroDecode_NullDestination_ThrowsArgumentNullException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:73` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:108 (`zero_decode_null_destination_throws_argument_null`)` | `translated` | `97197cffb02908653d831f3a62cdce2c1ddd52818343e9aacebc9b4c5ac1ee3e` |
@@ -273,7 +273,7 @@ Reviewed Rust tests live outside `generated_parity.rs` and carry a `parity-case`
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncode_DestinationTooSmall_ThrowsIndexOutOfRangeException::test` | `HelpersZeroCodingTests.ZeroEncode_DestinationTooSmall_ThrowsIndexOutOfRangeException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:218` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:222 (`zero_encode_destination_too_small_throws_index_out_of_range`)` | `translated` | `3870f0680f25e8df8de54f27c153feae9d8398e38531d67d6e0255cc34d50966` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncode_DestinationTooSmall_ThrowsIndexOutOfRangeException::test` | `HelpersZeroCodingTests.ZeroEncode_DestinationTooSmall_ThrowsIndexOutOfRangeException` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:218` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:222 (`zero_encode_destination_too_small_throws_index_out_of_range`)` | `translated` | `3870f0680f25e8df8de54f27c153feae9d8398e38531d67d6e0255cc34d50966` |
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncode_WithAppendedAcks_HandlesCorrectly::test` | `HelpersZeroCodingTests.ZeroEncode_WithAppendedAcks_HandlesCorrectly` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:231` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:235 (`zero_encode_with_appended_acks_preserves_count`)` | `translated` | `9803ec7bad46a1fd0f32c58d3ee08cf6a98b03a40a336ae231d1faf8211b273a` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncode_WithAppendedAcks_HandlesCorrectly::test` | `HelpersZeroCodingTests.ZeroEncode_WithAppendedAcks_HandlesCorrectly` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:231` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:235 (`zero_encode_with_appended_acks_preserves_count`)` | `translated` | `9803ec7bad46a1fd0f32c58d3ee08cf6a98b03a40a336ae231d1faf8211b273a` |
| `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncodeAndDecode_RoundTrip_PreservesData::test` | `HelpersZeroCodingTests.ZeroEncodeAndDecode_RoundTrip_PreservesData` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:254` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:248 (`zero_encode_decode_round_trip_preserves_patterns`)` | `translated` | `7847520873f87d798ea94fd1b6543db9d3606829a4bc3f4ec873e629fb6687eb` | | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs::HelpersZeroCodingTests.ZeroEncodeAndDecode_RoundTrip_PreservesData::test` | `HelpersZeroCodingTests.ZeroEncodeAndDecode_RoundTrip_PreservesData` | `` | `LibreMetaverse.Tests/HelpersZeroCodingTests.cs:254` | `Helpers` | `` | `tests/compat/tests/wire_semantics.rs:248 (`zero_encode_decode_round_trip_preserves_patterns`)` | `translated` | `7847520873f87d798ea94fd1b6543db9d3606829a4bc3f4ec873e629fb6687eb` |
| `LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs::HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow::test` | `HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow` | `` | `LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs:47` | `Http` | `` | `pending source translation` | `pending` | `9dbcf4023212af6f2294fa048068fe2f4d9e7e3f976d8eefb1dacbc9f6899aa8` | | `LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs::HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow::test` | `HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow` | `` | `LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs:47` | `Http` | `` | `tests/compat/tests/network_semantics.rs:306 (`put_response_with_non_http_location_does_not_throw`)` | `translated` | `9dbcf4023212af6f2294fa048068fe2f4d9e7e3f976d8eefb1dacbc9f6899aa8` |
| `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields::test` | `InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:37` | `` | `` | `pending source translation` | `pending` | `3ae365ed34f12d036dcd44c94f8568d163cfeec2a58b4975975b77b418770131` | | `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields::test` | `InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:37` | `` | `` | `pending source translation` | `pending` | `3ae365ed34f12d036dcd44c94f8568d163cfeec2a58b4975975b77b418770131` |
| `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems::test` | `InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:82` | `` | `` | `pending source translation` | `pending` | `d9e7c64106639071e1ec6ada7346bfa050a31183becaac79efefcd4755fe2855` | | `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems::test` | `InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:82` | `` | `` | `pending source translation` | `pending` | `d9e7c64106639071e1ec6ada7346bfa050a31183becaac79efefcd4755fe2855` |
| `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment::test` | `InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:122` | `` | `` | `pending source translation` | `pending` | `f807110d577c654128dd8d42e618c610c260c4f3367cf2795776564409f876a2` | | `LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment::test` | `InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment` | `` | `LibreMetaverse.Tests/InventoryAISClientTests.cs:122` | `` | `` | `pending source translation` | `pending` | `f807110d577c654128dd8d42e618c610c260c4f3367cf2795776564409f876a2` |
@@ -433,8 +433,8 @@ Reviewed Rust tests live outside `generated_parity.rs` and carry a `parity-case`
| `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment::test` | `MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:168` | `Inventory` | `` | `pending source translation` | `pending` | `f58debff8f66b7b417215eb6879f96935647b66cc619fdb00e6815c9ce5e7c67` | | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment::test` | `MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:168` | `Inventory` | `` | `pending source translation` | `pending` | `f58debff8f66b7b417215eb6879f96935647b66cc619fdb00e6815c9ce5e7c67` |
| `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject::test` | `MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:192` | `Inventory` | `` | `pending source translation` | `pending` | `b8137c994019103977b0b85d8c148e12ae8f9051334ca487db7c88bcbe707d40` | | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject::test` | `MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsObject_WhenTargetIsObject` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:192` | `Inventory` | `` | `pending source translation` | `pending` | `b8137c994019103977b0b85d8c148e12ae8f9051334ca487db7c88bcbe707d40` |
| `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test` | `MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:216` | `Inventory` | `` | `pending source translation` | `pending` | `6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b` | | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test` | `MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent` | `` | `LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs:216` | `Inventory` | `` | `pending source translation` | `pending` | `6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b` |
| `LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.DetectObjects::test` | `NetworkTests.DetectObjects` | `` | `LibreMetaverse.Tests/NetworkTests.cs:121` | `Network, RequiresLiveServer` | `` | `pending source translation` | `pending` | `8b72f1ca30feda2fad0812faf3a2f83d691d3cca9268054b3cc3bf89ad786e87` | | `LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.DetectObjects::test` | `NetworkTests.DetectObjects` | `` | `LibreMetaverse.Tests/NetworkTests.cs:121` | `Network, RequiresLiveServer` | `` | `tests/compat/tests/network_semantics.rs:436 (`detect_objects_live`)` | `ignored-live` | `8b72f1ca30feda2fad0812faf3a2f83d691d3cca9268054b3cc3bf89ad786e87` |
| `LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.CapsQueue::test` | `NetworkTests.CapsQueue` | `` | `LibreMetaverse.Tests/NetworkTests.cs:188` | `Network, RequiresLiveServer` | `` | `pending source translation` | `pending` | `cd5af4e7d69df3e938540711877d167fe28efb9af8af7518a62b8f406e46cc18` | | `LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.CapsQueue::test` | `NetworkTests.CapsQueue` | `` | `LibreMetaverse.Tests/NetworkTests.cs:188` | `Network, RequiresLiveServer` | `` | `tests/compat/tests/network_semantics.rs:515 (`caps_queue_live`)` | `ignored-live` | `cd5af4e7d69df3e938540711877d167fe28efb9af8af7518a62b8f406e46cc18` |
| `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.HelperFunctions::test` | `NotationSDTests.HelperFunctions` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:53` | `` | `` | `tests/compat/tests/structured_data.rs:622 (`notation_helper_functions`)` | `translated` | `a521ca5407ed3afd1d59f5b65a283bcebe9d34b4c384d68c5fc674b3a1eeb9df` | | `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.HelperFunctions::test` | `NotationSDTests.HelperFunctions` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:53` | `` | `` | `tests/compat/tests/structured_data.rs:622 (`notation_helper_functions`)` | `translated` | `a521ca5407ed3afd1d59f5b65a283bcebe9d34b4c384d68c5fc674b3a1eeb9df` |
| `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.DeserializeUndef::test` | `NotationSDTests.DeserializeUndef` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:80` | `` | `` | `tests/compat/tests/structured_data.rs:644 (`notation_deserialize_undef`)` | `translated` | `8c802fb14244cb423223c7c25702e4120da261f5dd1b378d2eca597277371406` | | `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.DeserializeUndef::test` | `NotationSDTests.DeserializeUndef` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:80` | `` | `` | `tests/compat/tests/structured_data.rs:644 (`notation_deserialize_undef`)` | `translated` | `8c802fb14244cb423223c7c25702e4120da261f5dd1b378d2eca597277371406` |
| `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.SerializeUndef::test` | `NotationSDTests.SerializeUndef` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:88` | `` | `` | `tests/compat/tests/structured_data.rs:652 (`notation_serialize_undef`)` | `translated` | `2ceee97b1826a3ba88a50798dba184b48998196192e31527171a9980f06c3281` | | `LibreMetaverse.Tests/NotationLLSDTests.cs::NotationSDTests.SerializeUndef::test` | `NotationSDTests.SerializeUndef` | `` | `LibreMetaverse.Tests/NotationLLSDTests.cs:88` | `` | `` | `tests/compat/tests/structured_data.rs:652 (`notation_serialize_undef`)` | `translated` | `2ceee97b1826a3ba88a50798dba184b48998196192e31527171a9980f06c3281` |

View File

@@ -13932,13 +13932,11 @@ fn member_02488(value: &mut libremetaverse::GridClient) {
// C# member: `P:LibreMetaverse.GridClient.Self`. // C# member: `P:LibreMetaverse.GridClient.Self`.
fn member_02489(value: &mut libremetaverse::GridClient) { fn member_02489(value: &mut libremetaverse::GridClient) {
let _ = value.self_(); let _ = value.self_();
let _ = value.set_self_(argument());
} }
// C# member: `P:LibreMetaverse.GridClient.Settings`. // C# member: `P:LibreMetaverse.GridClient.Settings`.
fn member_02490(value: &mut libremetaverse::GridClient) { fn member_02490(value: &mut libremetaverse::GridClient) {
let _ = value.settings(); let _ = value.settings();
let _ = value.set_settings(argument());
} }
// C# member: `P:LibreMetaverse.GridClient.Sound`. // C# member: `P:LibreMetaverse.GridClient.Sound`.

30
tests/compat/build.rs Normal file
View File

@@ -0,0 +1,30 @@
use std::path::Path;
const KEYS: [&str; 3] = ["GRID_USER", "GRID_PASSWORD", "GRID_LOGIN_URL"];
fn dotenv_has(path: &Path, key: &str) -> bool {
std::fs::read_to_string(path).is_ok_and(|contents| {
contents.lines().any(|line| {
let line = line.trim().strip_prefix("export ").unwrap_or(line.trim());
line.split_once('=').is_some_and(|(name, value)| {
name.trim() == key && !value.trim().trim_matches(['\'', '"']).is_empty()
})
})
})
}
fn main() {
let dotenv = Path::new(&std::env::var("CARGO_MANIFEST_DIR").expect("manifest directory"))
.join("../..")
.join(".env");
println!("cargo:rustc-check-cfg=cfg(live_grid_credentials)");
println!("cargo:rerun-if-changed={}", dotenv.display());
for key in KEYS {
println!("cargo:rerun-if-env-changed={key}");
}
if KEYS.iter().all(|key| {
std::env::var(key).is_ok_and(|value| !value.trim().is_empty()) || dotenv_has(&dotenv, key)
}) {
println!("cargo:rustc-cfg=live_grid_credentials");
}
}

View File

@@ -175,9 +175,6 @@
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite::test c512c54f78c46fb4774d9b5c9c1057627592e1c25267811cddf3d687d3d1ec4d // 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.AddRemovePolicy_ConcurrentAccess_DoesNotThrow::test a73a01065867d90305f491e41d9ea61bc27b80c026672ed920368877bfb0fe14
// pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test 697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7 // pending-parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test 697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7
// pending-parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully::test f8feb54d38f79624ddf1d368e0599259ff9d474c08ea0249a8f244460a1f533f
// pending-parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall::test dc3ffee1f6171833a8364750cfac2c6d3bfa812b404129e0425066ea212f2337
// pending-parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask::test 9eb3791b1def609d6f45be0fff8ae67ea04332045c0c419c6c2e7d118aeb6271
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test 5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4 // 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.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109 // pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109
@@ -219,8 +216,6 @@
// pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.Load_AutoDetectsGlbVsJson::test 6d07930be2302e504ef9ece3fef10f47b855d33ae6c8bff152e82df0beb2a474 // pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.Load_AutoDetectsGlbVsJson::test 6d07930be2302e504ef9ece3fef10f47b855d33ae6c8bff152e82df0beb2a474
// pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_TrsDefaults::test 0f0fbe116d20276c2aee917b940bded9de277ed0b3e3b659844c5b3532755259 // pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_TrsDefaults::test 0f0fbe116d20276c2aee917b940bded9de277ed0b3e3b659844c5b3532755259
// pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_ExplicitMatrix::test 197ba2ee3a66cbb7d927acea13ce9dc84e62af8a500d939957749039118755c8 // pending-parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_ExplicitMatrix::test 197ba2ee3a66cbb7d927acea13ce9dc84e62af8a500d939957749039118755c8
// pending-parity-case: LibreMetaverse.Tests/GridClientTests.cs::GridClientTests.GetGridRegion::test b0b72fc63fe829984167a479b763784c7daae8691399f3f3d2feb824090ad59f
// pending-parity-case: LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs::HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow::test 9dbcf4023212af6f2294fa048068fe2f4d9e7e3f976d8eefb1dacbc9f6899aa8
// pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields::test 3ae365ed34f12d036dcd44c94f8568d163cfeec2a58b4975975b77b418770131 // pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_ObjectAsset_ResultsInAttachmentInventoryTypeAndParsesFields::test 3ae365ed34f12d036dcd44c94f8568d163cfeec2a58b4975975b77b418770131
// pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems::test d9e7c64106639071e1ec6ada7346bfa050a31183becaac79efefcd4755fe2855 // pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseItemsFromEmbedded_ItemsKey_ParsesConcreteItems::test d9e7c64106639071e1ec6ada7346bfa050a31183becaac79efefcd4755fe2855
// pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment::test f807110d577c654128dd8d42e618c610c260c4f3367cf2795776564409f876a2 // pending-parity-case: LibreMetaverse.Tests/InventoryAISClientTests.cs::InventoryAISClientTests.ParseLinksFromEmbedded_MeshAsset_TreatedAsAttachment::test f807110d577c654128dd8d42e618c610c260c4f3367cf2795776564409f876a2
@@ -344,8 +339,6 @@
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_Indexer_ReturnsAttachment_WhenTargetIsAttachment::test f58debff8f66b7b417215eb6879f96935647b66cc619fdb00e6815c9ce5e7c67 // 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_Indexer_ReturnsObject_WhenTargetIsObject::test b8137c994019103977b0b85d8c148e12ae8f9051334ca487db7c88bcbe707d40
// pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test 6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b // pending-parity-case: LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs::MisclassifiedLinkRegressionTests.InventoryStore_NotContains_WhenTargetAbsent::test 6900e220c33ec135d02c55e047c7052a095d5dd9d71da4a857ae70bc151c472b
// pending-parity-case: LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.DetectObjects::test 8b72f1ca30feda2fad0812faf3a2f83d691d3cca9268054b3cc3bf89ad786e87
// pending-parity-case: LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.CapsQueue::test cd5af4e7d69df3e938540711877d167fe28efb9af8af7518a62b8f406e46cc18
// pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_StandardRegion_Loads256x256::test c4e3a994baf06b0106a6614e9c3efbcc27e45ff1725f5470d2db6ded1fff6014 // pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_StandardRegion_Loads256x256::test c4e3a994baf06b0106a6614e9c3efbcc27e45ff1725f5470d2db6ded1fff6014
// pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_Varregion_Loads512x512::test 4820338f10acbb4987e26992d7a7957be58ab6bc548674bf18943ef312962894 // pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_Varregion_Loads512x512::test 4820338f10acbb4987e26992d7a7957be58ab6bc548674bf18943ef312962894
// pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_WrongByteCount_FailsGracefully::test ff6fb4feb2773c93397d412d0ecf6aef1f3636e823dabb729a7a6cbe94d1513f // pending-parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_WrongByteCount_FailsGracefully::test ff6fb4feb2773c93397d412d0ecf6aef1f3636e823dabb729a7a6cbe94d1513f

View File

@@ -0,0 +1,600 @@
#![allow(clippy::needless_pass_by_value)]
// Exact fixture provenance at LibreMetaverse 2aa70bb68513b39795da5d13c88f31b86e85a3ba:
// DownloadManagerTests.cs 07f31d4db35335602bc8d0a70ef440d38ce7a59f2c7765a184bc33f03b6c6015
// GridClientTests.cs adfee7ccb929270d5e88dccdb354c686bd008a167a3a1028b536664813c6d1
// HttpCapsClientNonHttpLocationTests.cs 92cac055067808883132d7415dc87d17e6ad05a62b85cd454e552879f568016f
// NetworkTests.cs 325586ce3cf9af1dbcb8dd36fc2be5647e524f4baeeb260520e44e66172142dc
use libremetaverse::http::DownloadManager;
use libremetaverse::packets::PacketType;
use libremetaverse::{GridClient, GridLayerType, HttpCapsClient, NetworkManager};
use libremetaverse_compat_tests::block_on;
use libremetaverse_types::compat::{
CancellationToken, CancellationTokenSource, HttpMessageHandler, HttpRequest, HttpResponse, Uri,
};
use std::collections::BTreeMap;
use std::future::Future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, mpsc};
use std::task::{Context, Poll, Waker};
use std::thread;
use std::time::{Duration, Instant};
#[derive(Clone)]
struct FakeHandler {
requests: Arc<Mutex<Vec<HttpRequest>>>,
}
struct Delay {
state: Arc<Mutex<(bool, Option<Waker>)>>,
}
impl Delay {
fn new(duration: Duration) -> Self {
let state = Arc::new(Mutex::new((false, None::<Waker>)));
let worker_state = Arc::clone(&state);
thread::spawn(move || {
thread::sleep(duration);
let waker = {
let mut state = worker_state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.0 = true;
state.1.take()
};
if let Some(waker) = waker {
waker.wake();
}
});
Self { state }
}
}
impl Future for Delay {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
let mut state = self
.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if state.0 {
Poll::Ready(())
} else {
state.1 = Some(context.waker().clone());
Poll::Pending
}
}
}
impl FakeHandler {
fn new(data: &[u8], delay: Duration) -> (Self, HttpMessageHandler) {
let requests = Arc::new(Mutex::new(Vec::new()));
let recorded = Arc::clone(&requests);
let response = HttpResponse {
status_code: 200,
headers: BTreeMap::from([("Content-Length".into(), data.len().to_string())]),
content_type: None,
body: data.to_vec(),
};
let handler = HttpMessageHandler::new(move |request, _cancellation_token| {
let recorded = Arc::clone(&recorded);
let response = response.clone();
async move {
recorded
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push(request);
if !delay.is_zero() {
Delay::new(delay).await;
}
response
}
});
(Self { requests }, handler)
}
fn send_count(&self) -> usize {
self.requests
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.len()
}
fn only_request(&self) -> HttpRequest {
let requests = self
.requests
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
assert_eq!(requests.len(), 1, "expected exactly one HTTP request");
requests[0].clone()
}
}
fn block_on_timeout<F>(future: F, timeout: Duration) -> F::Output
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let (sender, receiver) = mpsc::sync_channel(1);
let worker = thread::spawn(move || {
let _ = sender.send(block_on(future));
});
let output = receiver.recv_timeout(timeout).expect("operation timed out");
worker.join().expect("operation worker panicked");
output
}
async fn join<F1, F2>(first: F1, second: F2) -> (F1::Output, F2::Output)
where
F1: Future,
F2: Future,
{
let mut first = std::pin::pin!(first);
let mut second = std::pin::pin!(second);
let mut first_output = None;
let mut second_output = None;
std::future::poll_fn(|context| {
if first_output.is_none()
&& let Poll::Ready(output) = first.as_mut().poll(context)
{
first_output = Some(output);
}
if second_output.is_none()
&& let Poll::Ready(output) = second.as_mut().poll(context)
{
second_output = Some(output);
}
if first_output.is_some() && second_output.is_some() {
Poll::Ready((
first_output.take().expect("first future output"),
second_output.take().expect("second future output"),
))
} else {
Poll::Pending
}
})
.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)
}
// parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_SingleDownload_CompletesSuccessfully::test f8feb54d38f79624ddf1d368e0599259ff9d474c08ea0249a8f244460a1f533f translated
#[test]
fn queue_download_single_completes_successfully() {
let bytes = b"hello world".to_vec();
let (handler, message_handler) = FakeHandler::new(&bytes, Duration::ZERO);
let http_caps = HttpCapsClient::new(message_handler).expect("HttpCapsClient constructor");
let mut client = GridClient::new().expect("GridClient constructor");
client.set_http_caps_client(http_caps);
let downloads = Arc::new(DownloadManager::new(client).expect("DownloadManager constructor"));
let worker_downloads = Arc::clone(&downloads);
let result = block_on_timeout(
async move {
worker_downloads
.queue_download_with_uri_string_i_progress_cancellation_token_int32(
Uri("http://example.test/one".into()),
None,
None,
Some(CancellationToken::default()),
Some(1),
)
.await
},
Duration::from_secs(5),
);
let dispose_result = downloads.dispose();
let (response, data) = result.expect("QueueDownloadAsync");
assert_eq!(data, bytes);
assert!(response.is_success_status_code());
assert_eq!(response.headers["Content-Length"], bytes.len().to_string());
let request = handler.only_request();
assert_eq!(request.method, "GET");
assert_eq!(request.uri, Uri("http://example.test/one".into()));
dispose_result.expect("DownloadManager Dispose");
}
// parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_DeduplicatesRequests_SameUri_OneHttpCall::test dc3ffee1f6171833a8364750cfac2c6d3bfa812b404129e0425066ea212f2337 translated
#[test]
fn queue_download_deduplicates_same_uri() {
let bytes = b"duplicate payload".to_vec();
let (handler, message_handler) = FakeHandler::new(&bytes, Duration::from_millis(200));
let http_caps = HttpCapsClient::new(message_handler).expect("HttpCapsClient constructor");
let mut client = GridClient::new().expect("GridClient constructor");
client.set_http_caps_client(http_caps);
let downloads = Arc::new(DownloadManager::new(client).expect("DownloadManager constructor"));
let uri = Uri("http://example.test/dup".into());
let worker_downloads = Arc::clone(&downloads);
let (first_result, second_result) = block_on_timeout(
async move {
let first = worker_downloads
.queue_download_with_uri_string_i_progress_cancellation_token_int32(
uri.clone(),
None,
None,
Some(CancellationToken::default()),
Some(1),
);
let second = worker_downloads
.queue_download_with_uri_string_i_progress_cancellation_token_int32(
uri,
None,
None,
Some(CancellationToken::default()),
Some(1),
);
join(first, second).await
},
Duration::from_secs(5),
);
let dispose_result = downloads.dispose();
let (_, first_data) = first_result.expect("first download");
let (_, second_data) = second_result.expect("second download");
assert_eq!(first_data, bytes);
assert_eq!(second_data, bytes);
assert_eq!(handler.send_count(), 1);
let request = handler.only_request();
assert_eq!(request.method, "GET");
assert_eq!(request.uri, Uri("http://example.test/dup".into()));
dispose_result.expect("DownloadManager Dispose");
}
// parity-case: LibreMetaverse.Tests/DownloadManagerTests.cs::DownloadManagerTests.QueueDownloadAsync_Cancellation_PropagatesToTask::test 9eb3791b1def609d6f45be0fff8ae67ea04332045c0c419c6c2e7d118aeb6271 translated
#[test]
fn queue_download_cancellation_propagates() {
let bytes = b"will cancel".to_vec();
let (handler, message_handler) = FakeHandler::new(&bytes, Duration::ZERO);
let http_caps = HttpCapsClient::new(message_handler).expect("HttpCapsClient constructor");
let mut client = GridClient::new().expect("GridClient constructor");
client.set_http_caps_client(http_caps);
let downloads = DownloadManager::new(client).expect("DownloadManager constructor");
let cancellation = CancellationTokenSource::new();
cancellation.cancel();
let result = block_on(
downloads.queue_download_with_uri_string_i_progress_cancellation_token_int32(
Uri("http://example.test/cancel".into()),
None,
None,
Some(cancellation.token()),
Some(1),
),
);
let dispose_result = downloads.dispose();
assert!(
matches!(&result, Err(libremetaverse::Error::Cancelled)),
"expected Cancelled, got {result:?}"
);
assert_eq!(handler.send_count(), 0);
dispose_result.expect("DownloadManager Dispose");
}
// parity-case: LibreMetaverse.Tests/HttpCapsClientNonHttpLocationTests.cs::HttpCapsClientNonHttpLocationTests.PutAsync_ResponseWithNonHttpLocationHeader_DoesNotThrow::test 9dbcf4023212af6f2294fa048068fe2f4d9e7e3f976d8eefb1dacbc9f6899aa8 translated
#[test]
fn put_response_with_non_http_location_does_not_throw() {
const SLCAPS: &str = "slcaps://11111111-1111-1111-1111-111111111111/category/x";
let body = b"<llsd><map/></llsd>";
let response = HttpResponse {
status_code: 201,
headers: BTreeMap::from([
("Location".into(), SLCAPS.into()),
("Content-Location".into(), SLCAPS.into()),
("Content-Length".into(), body.len().to_string()),
]),
content_type: Some("application/llsd+xml".into()),
body: body.to_vec(),
};
let requests = Arc::new(Mutex::new(Vec::new()));
let recorded = Arc::clone(&requests);
let message_handler = HttpMessageHandler::new(move |request, _cancellation_token| {
let recorded = Arc::clone(&recorded);
let response = response.clone();
async move {
recorded
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push(request);
response
}
});
let mut client = GridClient::new().expect("GridClient constructor");
let http_caps = HttpCapsClient::new(message_handler).expect("HttpCapsClient constructor");
client.set_http_caps_client(http_caps);
let (reply, reply_body) = block_on(
client
.http_caps_client()
.put_with_uri_string_bytes_cancellation_token_i_progress(
Uri("http://127.0.0.1/".into()),
"application/llsd+xml".into(),
b"test".to_vec(),
CancellationToken::default(),
None,
),
)
.expect("HttpCapsClient PutAsync");
assert_eq!(reply.status_code, 201);
assert_eq!(reply.headers["Location"], SLCAPS);
assert_eq!(reply.headers["Content-Location"], SLCAPS);
assert_eq!(reply.content_type.as_deref(), Some("application/llsd+xml"));
assert_eq!(reply_body, body);
let request = {
let requests = requests
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
assert_eq!(requests.len(), 1, "expected exactly one HTTP request");
requests[0].clone()
};
assert_eq!(request.method, "PUT");
assert_eq!(request.uri, Uri("http://127.0.0.1/".into()));
assert_eq!(
request.content_type.as_deref(),
Some("application/llsd+xml")
);
assert_eq!(request.body, b"test");
}
// parity-case: LibreMetaverse.Tests/GridClientTests.cs::GridClientTests.GetGridRegion::test b0b72fc63fe829984167a479b763784c7daae8691399f3f3d2feb824090ad59f 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_grid_region_live() {
let mut client = GridClient::new().expect("GridClient constructor");
client.self_().movement.set_fly(true);
let network = client.network();
let (first, last, password, login_url) = live_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(),
"contact@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 log in: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
let simulator = network
.current_sim()
.expect("CurrentSim is null after successful login");
if simulator.name.is_empty() {
eprintln!("CurrentSim.Name is empty after login, proceeding with tests");
} else if !simulator.name.eq_ignore_ascii_case("hooper") {
eprintln!(
"logged in to region '{}' instead of 'Hooper', proceeding with tests",
simulator.name
);
}
let region = block_on(
client
.grid()
.get_grid_region_with_string_grid_layer_type_cancellation_token(
"Hippo Hollow".into(),
GridLayerType::Terrain,
None,
),
)
.expect("GridManager GetGridRegionAsync");
if let Some(Some(region)) = region {
assert_eq!(region.name.to_lowercase(), "hippo hollow");
} else {
eprintln!("Grid region 'Hippo Hollow' not found; skipping assertion");
}
network.logout_with_method().expect("NetworkManager Logout");
let _ = client.dispose_with_method();
}
// parity-case: LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.DetectObjects::test 8b72f1ca30feda2fad0812faf3a2f83d691d3cca9268054b3cc3bf89ad786e87 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 detect_objects_live() {
let mut client = GridClient::new().expect("GridClient constructor");
client.settings().timing().login_timeout = 30 * 1_000;
client.self_().movement.set_fly(true);
let detected = Arc::new(AtomicBool::new(false));
let callback_detected = Arc::clone(&detected);
let network = Arc::new(client.network());
network
.register_callback_with_packet_type_event_handler(
PacketType::ObjectUpdate,
Arc::new(move |_event| callback_detected.store(true, Ordering::SeqCst)),
)
.expect("register ObjectUpdate callback");
let (first, last, password, login_url) = live_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 login_network = Arc::clone(&network);
let logged_in = block_on_timeout(
async move {
login_network
.login_with_login_params_cancellation_token(login, None)
.await
},
Duration::from_secs(45),
)
.expect("NetworkManager LoginAsync");
assert!(
logged_in,
"client failed to log in: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
thread::sleep(Duration::from_secs(1));
let simulator = network
.current_sim()
.expect("CurrentSim is null after successful login");
if simulator.name.is_empty() {
eprintln!("CurrentSim.Name is empty, but proceeding with tests");
} else if !simulator.name.eq_ignore_ascii_case("hooper") {
eprintln!(
"logged in to region '{}' instead of 'Hooper', but proceeding with tests",
simulator.name
);
}
let started = Instant::now();
while !detected.load(Ordering::SeqCst) {
assert!(
started.elapsed() <= Duration::from_secs(20),
"timeout waiting for an ObjectUpdate packet after 20 seconds"
);
thread::sleep(Duration::from_millis(100));
}
assert!(
detected.load(Ordering::SeqCst),
"successfully detected objects"
);
network.logout_with_method().expect("NetworkManager Logout");
let _ = client.dispose_with_method();
}
// parity-case: LibreMetaverse.Tests/NetworkTests.cs::NetworkTests.CapsQueue::test cd5af4e7d69df3e938540711877d167fe28efb9af8af7518a62b8f406e46cc18 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 caps_queue_live() {
let mut client = GridClient::new().expect("GridClient constructor");
client.settings().timing().login_timeout = 30 * 1_000;
client.self_().movement.set_fly(true);
let detected = Arc::new(AtomicBool::new(false));
let callback_detected = Arc::clone(&detected);
let network = Arc::new(client.network());
network
.register_callback_with_packet_type_event_handler(
PacketType::ObjectUpdate,
Arc::new(move |_event| callback_detected.store(true, Ordering::SeqCst)),
)
.expect("register ObjectUpdate callback");
let (first, last, password, login_url) = live_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 login_network = Arc::clone(&network);
let logged_in = block_on_timeout(
async move {
login_network
.login_with_login_params_cancellation_token(login, None)
.await
},
Duration::from_secs(45),
)
.expect("NetworkManager LoginAsync");
assert!(
logged_in,
"client failed to log in: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
thread::sleep(Duration::from_secs(1));
let simulator = network
.current_sim()
.expect("CurrentSim is null after successful login");
if simulator.name.is_empty() {
eprintln!("CurrentSim.Name is empty, but proceeding with tests");
} else if !simulator.name.eq_ignore_ascii_case("hooper") {
eprintln!(
"logged in to region '{}' instead of 'Hooper', but proceeding with tests",
simulator.name
);
}
let already_running = network
.current_sim()
.and_then(|simulator| simulator.caps)
.is_some_and(|caps| caps.is_event_queue_running());
let event_queue_running = if already_running {
true
} else {
let (sender, receiver) = mpsc::sync_channel(1);
let _subscription = network.subscribe_event_queue_running(Arc::new(move |_event| {
let _ = sender.send(());
}));
receiver
.recv_timeout(Duration::from_secs(10))
.expect("timeout waiting for event queue to start");
true
};
assert!(
event_queue_running,
"CAPS Event Queue is not running and failed to start"
);
network.logout_with_method().expect("NetworkManager Logout");
let _ = client.dispose_with_method();
}

File diff suppressed because it is too large Load Diff

View File

@@ -188,6 +188,20 @@ MEMBER_SIGNATURE_OVERRIDES = {
"mutable_self,owned,mutable_borrow,mutable_borrow,optional_mutable_borrow", "mutable_self,owned,mutable_borrow,mutable_borrow,optional_mutable_borrow",
), ),
} }
PROPERTY_SIGNATURE_OVERRIDES = {
"P:LibreMetaverse.GridClient.Self": (
"pub fn self_(&mut self) -> &mut libremetaverse::AgentManager",
"mutable_self",
),
"P:LibreMetaverse.GridClient.Settings": (
"pub fn settings(&mut self) -> &mut libremetaverse::Settings",
"mutable_self",
),
"P:LibreMetaverse.Settings.Timing": (
"pub fn timing(&mut self) -> &mut libremetaverse::TimingSettings",
"mutable_self",
),
}
TYPE_USAGE_OVERRIDES = { TYPE_USAGE_OVERRIDES = {
"LibreMetaverse.DictionaryChangeCallback": ( "LibreMetaverse.DictionaryChangeCallback": (
"std::sync::Arc<dyn Fn(libremetaverse::DictionaryEventAction, " "std::sync::Arc<dyn Fn(libremetaverse::DictionaryEventAction, "
@@ -673,6 +687,9 @@ def member_signature(mapper: Mapper, owner: dict, item: dict, rust_name: str) ->
signature = f"pub {rust_name}: {value_type}" signature = f"pub {rust_name}: {value_type}"
return signature, "owned_value", asyncness, "none", "direct" return signature, "owned_value", asyncness, "none", "direct"
if kind in {"property", "indexer"}: if kind in {"property", "indexer"}:
if item["doc_id"] in PROPERTY_SIGNATURE_OVERRIDES:
signature, ownership = PROPERTY_SIGNATURE_OVERRIDES[item["doc_id"]]
return signature, ownership, asyncness, error_model, "accessor_methods"
value_type = mapper.type(item["type"], item.get("nullability"), generics) value_type = mapper.type(item["type"], item.get("nullability"), generics)
accessors = {accessor["kind"] for accessor in item.get("accessors", [])} accessors = {accessor["kind"] for accessor in item.get("accessors", [])}
pieces = [] pieces = []