Translate asset and archive parity tests

This commit is contained in:
2026-08-08 19:01:17 +02:00
parent 6d4d9adaf4
commit 21bbdab9ab
13 changed files with 2941 additions and 1481 deletions

View File

@@ -51,6 +51,8 @@ pub enum Error {
ArgumentNull,
/// An argument value or length was invalid.
Argument,
/// The requested operation is invalid in the current object state.
InvalidOperation,
/// An index or destination buffer boundary was exceeded.
IndexOutOfRange,
/// An operation observed a requested cancellation.
@@ -63,7 +65,11 @@ impl Error {
pub const fn csharp_member(self) -> Option<&'static str> {
match self {
Self::NotImplemented(error) => Some(error.csharp_member()),
Self::ArgumentNull | Self::Argument | Self::IndexOutOfRange | Self::Cancelled => None,
Self::ArgumentNull
| Self::Argument
| Self::InvalidOperation
| Self::IndexOutOfRange
| Self::Cancelled => None,
}
}
}
@@ -80,6 +86,9 @@ impl fmt::Display for Error {
Self::NotImplemented(error) => error.fmt(formatter),
Self::ArgumentNull => formatter.write_str("required argument was null"),
Self::Argument => formatter.write_str("argument was invalid"),
Self::InvalidOperation => {
formatter.write_str("operation was invalid for the current state")
}
Self::IndexOutOfRange => formatter.write_str("index was out of range"),
Self::Cancelled => formatter.write_str("operation was cancelled"),
}

View File

@@ -0,0 +1,114 @@
// The upstream tests reach the internal OarFile.LoadTerrain member via reflection.
// Keep the equivalent Rust seam private to the owning crate.
// BSD-3-Clause source (see LICENSE.md): LibreMetaverse.Tests/OarFileTerrainTests.cs
// SHA-256: 19c42f7f132b97dc1667208f1e9b49df8ff6db96265c22b1c1ef5498e3c5c75d
#![allow(clippy::float_cmp)]
use crate::assets::OarFile;
use std::sync::{Arc, Mutex};
fn raw_terrain(side: usize, fill: f32) -> Vec<u8> {
fill.to_le_bytes().repeat(side * side)
}
// Fixture SHA-256: fcdd41fbadfe6a69d9687b3777692c2de82c831856fa8bcef978c3c17b0117fa
// parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_StandardRegion_Loads256x256::test c4e3a994baf06b0106a6614e9c3efbcc27e45ff1725f5470d2db6ded1fff6014 translated
#[test]
fn load_standard_region_terrain() {
let data = raw_terrain(256, 21.0);
let total_bytes = i64::try_from(data.len()).expect("terrain byte count fits i64");
let captured = Arc::new(Mutex::new(None));
let callback_capture = Arc::clone(&captured);
let loaded = OarFile::load_terrain(
"terrains/region.r32",
data,
Box::new(move |terrain, _, _| {
*callback_capture.lock().expect("terrain capture") = Some(terrain);
}),
0,
total_bytes,
)
.expect("OarFile.LoadTerrain");
assert!(loaded);
let terrain = captured
.lock()
.expect("terrain capture")
.take()
.expect("terrain callback");
assert_eq!(terrain.len(), 256);
assert!(terrain.iter().all(|row| row.len() == 256));
assert_eq!(terrain[0][0], 21.0);
}
// Fixture SHA-256: c07500b6f966d65dda9261e3e701fb5bb13e31239ff3a89d74c0046d0ba4ec12
// parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_Varregion_Loads512x512::test 4820338f10acbb4987e26992d7a7957be58ab6bc548674bf18943ef312962894 translated
#[test]
fn load_varregion_terrain() {
let data = raw_terrain(512, 30.0);
let total_bytes = i64::try_from(data.len()).expect("terrain byte count fits i64");
let captured = Arc::new(Mutex::new(None));
let callback_capture = Arc::clone(&captured);
let loaded = OarFile::load_terrain(
"terrains/megaregion.f32",
data,
Box::new(move |terrain, _, _| {
*callback_capture.lock().expect("terrain capture") = Some(terrain);
}),
0,
total_bytes,
)
.expect("OarFile.LoadTerrain");
assert!(loaded);
let terrain = captured
.lock()
.expect("terrain capture")
.take()
.expect("terrain callback");
assert_eq!(terrain.len(), 512);
assert!(terrain.iter().all(|row| row.len() == 512));
}
// Fixture SHA-256: 2dc88437e482fdaa5613df885d49c2b19bd623ecfc387a72338d2367d4b3b451
// parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_WrongByteCount_FailsGracefully::test ff6fb4feb2773c93397d412d0ecf6aef1f3636e823dabb729a7a6cbe94d1513f translated
#[test]
fn reject_terrain_with_wrong_byte_count() {
let data = vec![0; 256_usize * 256 * 4 + 3];
let total_bytes = i64::try_from(data.len()).expect("terrain byte count fits i64");
let captured = Arc::new(Mutex::new(None));
let callback_capture = Arc::clone(&captured);
let loaded = OarFile::load_terrain(
"terrains/broken.r32",
data,
Box::new(move |terrain, _, _| {
*callback_capture.lock().expect("terrain capture") = Some(terrain);
}),
0,
total_bytes,
)
.expect("OarFile.LoadTerrain");
assert!(!loaded);
assert!(captured.lock().expect("terrain capture").is_none());
}
// Fixture SHA-256: 9b94b83334ada38b48b53f19ef2c45abc9f84fb723564671290a556f75d6fdc8
// parity-case: LibreMetaverse.Tests/OarFileTerrainTests.cs::OarFileTerrainTests.LoadTerrain_NonSquarePostCount_FailsGracefully::test 218754e53232cd10318668c70b94ae9d7d541d09598b0490082e023e76d6045f translated
#[test]
fn reject_terrain_with_non_square_post_count() {
let data = vec![0; 257_usize * 256 * 4];
let total_bytes = i64::try_from(data.len()).expect("terrain byte count fits i64");
let captured = Arc::new(Mutex::new(None));
let callback_capture = Arc::clone(&captured);
let loaded = OarFile::load_terrain(
"terrains/nonsquare.r32",
data,
Box::new(move |terrain, _, _| {
*callback_capture.lock().expect("terrain capture") = Some(terrain);
}),
0,
total_bytes,
)
.expect("OarFile.LoadTerrain");
assert!(!loaded);
assert!(captured.lock().expect("terrain capture").is_none());
}

View File

@@ -5080,7 +5080,7 @@ impl AssetManager {
libremetaverse_types::unimplemented_api!("E:LibreMetaverse.AssetManager.XferReceived")
}
/// C# member: `M:LibreMetaverse.AssetManager.#ctor(LibreMetaverse.GridClient)`.
pub fn new(client: libremetaverse::GridClient) -> Result<Self, crate::Error> {
pub fn new(client: Option<libremetaverse::GridClient>) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AssetManager.#ctor(LibreMetaverse.GridClient)",
)
@@ -14988,7 +14988,10 @@ impl InventoryException {
}
/// C# type: `T:LibreMetaverse.InventoryFolder`.
pub struct InventoryFolder;
pub struct InventoryFolder {
/// Rust composition for C# base type `LibreMetaverse.InventoryBase`.
pub base: libremetaverse::InventoryBase,
}
impl InventoryFolder {
/// C# member: `F:LibreMetaverse.InventoryFolder.VERSION_UNKNOWN`.
pub const VERSION_UNKNOWN: i32 = -1;
@@ -27671,7 +27674,7 @@ impl TerrainManager {
pub async fn get_terrain_material_overrides(
&self,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Option<Vec<libremetaverse::assets::AssetMaterial>>, crate::Error> {
) -> Result<Option<Vec<Option<libremetaverse::assets::AssetMaterial>>>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)",
)
@@ -27679,7 +27682,7 @@ impl TerrainManager {
/// C# member: `M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)`.
pub async fn set_terrain_material_overrides(
&self,
overrides: Vec<libremetaverse::assets::AssetMaterial>,
overrides: Vec<Option<libremetaverse::assets::AssetMaterial>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
@@ -29952,7 +29955,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.ApplyOverride(LibreMetaverse.Assets.AssetMaterial)`.
pub fn apply_override(
&self,
&mut self,
override_mat: libremetaverse::assets::AssetMaterial,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
@@ -29960,11 +29963,11 @@ pub mod assets {
)
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.Decode`.
pub fn decode(&self) -> Result<bool, crate::Error> {
pub fn decode(&mut self) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Assets.AssetMaterial.Decode")
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.Encode`.
pub fn encode(&self) -> Result<(), crate::Error> {
pub fn encode(&mut self) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Assets.AssetMaterial.Encode")
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.FromOverrideOsd(LibreMetaverse.StructuredData.OSDMap)`.
@@ -29977,7 +29980,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetAlphaCutoff(System.Single,System.Boolean)`.
pub fn set_alpha_cutoff_with_single_boolean(
&self,
&mut self,
cutoff: f32,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -29987,7 +29990,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetAlphaMode(LibreMetaverse.Assets.GltfAlphaMode,System.Boolean)`.
pub fn set_alpha_mode_with_gltf_alpha_mode_boolean(
&self,
&mut self,
mode: libremetaverse::assets::GltfAlphaMode,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -29997,7 +30000,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetBaseColorFactor(LibreMetaverse.Color4,System.Boolean)`.
pub fn set_base_color_factor_with_color4_boolean(
&self,
&mut self,
color: libremetaverse_types::Color4,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -30006,14 +30009,14 @@ pub mod assets {
)
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetBaseMaterial`.
pub fn set_base_material(&self) -> Result<(), crate::Error> {
pub fn set_base_material(&mut self) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Assets.AssetMaterial.SetBaseMaterial",
)
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetDoubleSided(System.Boolean,System.Boolean)`.
pub fn set_double_sided_with_boolean_boolean(
&self,
&mut self,
double_sided: bool,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -30023,7 +30026,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetEmissiveFactor(LibreMetaverse.Vector3,System.Boolean)`.
pub fn set_emissive_factor_with_vector3_boolean(
&self,
&mut self,
color: libremetaverse_types::Vector3,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -30033,7 +30036,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetMetallicFactor(System.Single,System.Boolean)`.
pub fn set_metallic_factor_with_single_boolean(
&self,
&mut self,
metallic: f32,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -30043,7 +30046,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetRoughnessFactor(System.Single,System.Boolean)`.
pub fn set_roughness_factor_with_single_boolean(
&self,
&mut self,
roughness: f32,
for_override: Option<bool>,
) -> Result<(), crate::Error> {
@@ -30053,7 +30056,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetTextureId(System.Int32,LibreMetaverse.UUID,System.Boolean)`.
pub fn set_texture_id(
&self,
&mut self,
slot: i32,
id: libremetaverse_types::UUID,
for_override: Option<bool>,
@@ -30064,7 +30067,7 @@ pub mod assets {
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetTextureOffset(System.Int32,LibreMetaverse.Vector2)`.
pub fn set_texture_offset(
&self,
&mut self,
slot: i32,
offset: libremetaverse_types::Vector2,
) -> Result<(), crate::Error> {
@@ -30073,14 +30076,18 @@ pub mod assets {
)
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetTextureRotation(System.Int32,System.Single)`.
pub fn set_texture_rotation(&self, slot: i32, rotation: f32) -> Result<(), crate::Error> {
pub fn set_texture_rotation(
&mut self,
slot: i32,
rotation: f32,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Assets.AssetMaterial.SetTextureRotation(System.Int32,System.Single)",
)
}
/// C# member: `M:LibreMetaverse.Assets.AssetMaterial.SetTextureScale(System.Int32,LibreMetaverse.Vector2)`.
pub fn set_texture_scale(
&self,
&mut self,
slot: i32,
scale: libremetaverse_types::Vector2,
) -> Result<(), crate::Error> {

View File

@@ -4,6 +4,27 @@ extern crate self as libremetaverse;
mod generated;
#[cfg(test)]
mod asset_archive_semantics;
type Terrain = Vec<Vec<f32>>;
type TerrainLoaded = Box<dyn Fn(Terrain, i64, i64) + Send + Sync>;
impl assets::OarFile {
#[allow(dead_code)]
fn load_terrain(
_file_path: &str,
_data: Vec<u8>,
_callback: TerrainLoaded,
_bytes_read: i64,
_total_bytes: i64,
) -> Result<bool, Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Assets.OarFile.LoadTerrain(System.String,System.Byte[],LibreMetaverse.Assets.OarFile.TerrainLoadedCallback,System.Int64,System.Int64)",
)
}
}
pub use generated::*;
pub use libremetaverse_imaging as imaging_abstractions;
pub use libremetaverse_structured_data as structured_data;