Translate world-state parity tests

This commit is contained in:
2026-08-08 21:28:18 +02:00
parent d7d12957e6
commit cdb392643c
18 changed files with 3606 additions and 1509 deletions

View File

@@ -2,30 +2,8 @@
// Source: LibreMetaverse 2aa70bb68513b39795da5d13c88f31b86e85a3ba
// Pending cases are catalog entries, not fake executable Rust tests.
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.GridManager_Null_Throws::test e972a95f334b0f46948cda34c27124c28c44faf4372d0e598d7dc60050b49be4
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AppearanceManager_Null_Throws::test a3c872805bc924b5a1a9441d5c9eb61ed6d6e9477e4bb411131359290b144ab4
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.ObjectManager_Null_Throws::test 8a11feba0edfb35e5dd99f681182e8f5a5cf8a3f738211b45a683a8a2ad45ddb
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AgentManager_Null_Throws::test d86e41b6cdd03063685aa330c31b9a274c1d400b9a461abf3065fe185fe0bbf0
// pending-parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AvatarManager_Null_Throws::test 54f23cbf0ba87b0525c2914e78d75808e93fd1c460fc0bec4d15f09575b1431c
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test 5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109
// pending-parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.ResetParcelEnvironment_SendsHttpDeleteNotPost::test 4360439192c137f5d7a2ceb83b8664f3917b4b261798050cb855d596f483f082
// pending-parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_ParsesBlockedTrustedAndAllowedLists::test 49a77aa9d7c5bf1232082a08594e7ebcfafad11ab110c9e15d2ad4ea1831c649
// pending-parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_AllEmptyLists_ProducesEmptyResult::test 711eaebb11f5d7ece92946d3de240a341a4b1fd8f30617fd97cb059d0dc6a0b9
// pending-parity-case: LibreMetaverse.Tests/EstateToolsTests.cs::EstateToolsTests.RequestConvenant::test d8d752dd6e53477e4c66bcb5d0e3ec1021a9b3ceda1f689f59b5e606e1713729
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Deserialize_ReadsExperienceIdsKey::test aa1f7d09f6c79118ea2e60621d57bd63632085ce81bc979e42294aca53d4f0d9
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Deserialize_IgnoresLegacyExperienceKeysKey::test cd1d372b9a976c04dee11da1ade182e4f9239e1f7633e788347c6d5f255835ec
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Serialize_WritesExperienceIdsKey::test 3fa4cb5d598940f693285bcc64179a4dea927a3643a76a061a933a265488d4be
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Deserialize_ReadsBlockedTrustedAllowedAndDefault::test 61df3b0c60d32b6c9bb843eb7a1a11fe8c0b1fd59c7a09853ad1b0e1d93cd4fd
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Deserialize_IgnoresLegacyContribKey::test 851d037919003ebb414105e57bf37f73ba37dff8f733c36183c447f1349f3316
// pending-parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Serialize_WritesAllowedKeyNotContrib::test 53c3a7d6a200816615a9efaa222ccf294e3a8334d320812c6ec56a0a3e7e05bb
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Deserialize_ReadsAllowedListFromExperiencesKey::test 9bab78dbe142a3da6ad21a278f25d7da90776fce3861c7d602203c1e1842c9af
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Deserialize_IgnoresLegacyAllowedKey::test ec6d5e7c81214ea4e3d99740fdd33961578f895a90a42082067ca9819d93d2b0
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Serialize_WritesAllowedListUnderExperiencesKey::test 45a0bffe39ab7bf0734ec5fce4435dbdda7ae94cf4e933210669e91dc45b1baf
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Allowed_ReturnsAllow::test cf095ab428713004ebac6e1915a47ae0c03bd2958addbba2a39fd2f0edf51a74
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Blocked_ReturnsBlock::test af084d1ab7a11079e6251865e8fd5aecfde38d2df359ebc1e208bc74925935c0
// pending-parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_NeitherList_ReturnsForget::test 93204469ecfec2f3f4eb6838c99f6e2baf09bf76a075958d1ed369ba03767158
// pending-parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_ListingsRootFolder_ReturnsListingsRoot::test a3c21e19f21924bffbbafe1199a4b942b545bdb278c3362b2a1ec98b35d672cf
// pending-parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_ListingFolder_ReturnsListing::test c08d860a4506254d9ac831e91894f2fc34d840c100468ee7e39b505690302368
// pending-parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_VersionFolder_ReturnsVersion::test 90d792d6858c6a253b9ddf80d3f5e7244570490fa4ef68c1914b342db05c7aa2
@@ -86,28 +64,8 @@
// pending-parity-case: LibreMetaverse.Tests/MarketplaceManagerTests.cs::MarketplaceManagerTests.MarketplaceListing_DefaultValues_AreCorrect::test 70a29163581702fdc32c54a398ad4cefa608da8b1bc9d54a4e9e191e95398fb7
// pending-parity-case: LibreMetaverse.Tests/OfflineMessagesTests.cs::OfflineMessagesTests.RetrieveInstantMessagesAsync_BareArrayOfArraysResponse_RaisesInstantMessage::test d51f69e0fa1e1e59a5cedec7d60315d1f590848672a9f4f0c5e21d2abff5c372
// pending-parity-case: LibreMetaverse.Tests/OfflineMessagesTests.cs::OfflineMessagesTests.RetrieveInstantMessagesAsync_MessagesMapResponse_RaisesInstantMessage::test dbce6320d57d12ca5557cfba65257e15be314cde947b20dd21bc386819fcb3ce
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathBegin::test 5c8e14e1d08a91a4e7c3e252c932b8fe15cf6b5231cc9fe5bb7ef2ae6e2c8299
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathEnd::test 693bfe29f448e433705d65b8b6907f1b270fd636f914e18b76d7a0047dbea506
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathRevolutions::test c3f61c80a204e608976cb44a0c10b9651496074c25f254eec8621db65dbbbe37
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathScale::test b92e4ab375fcc788dea701d8579268f39b4f3b1d51d4eed91458547350405d61
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathTaper::test 93a0adbc0630de3c9fbea6f8080d74ef6e3180e3813f00460bce5d52ef2f12b7
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntryOffsets::test 720ea1a9477d333b14786bc14e9abca24858e63acae94f4c6055d81f14ff033a
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry::test 0670a9a8dcc4001e726df699ef8001604956b6674028a192502b92b725858990
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_DefaultFace_Roundtrips::test 8db56b448e53e6bec5540a5a4721dc75f85a933c6c89e166f7df7baf8e44aab3
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_PerFace_Roundtrips::test 2897366ecaa41f07b4db6ebf3e80a7ebe56480f1c44f31cedea2d88a62214dc6
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_MultipleFaces_Roundtrips::test f3d8c103e4b3d43a8e3bea1356ba13808633fdb2ea06583234551f9059b2a241
// pending-parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_InheritsFromDefault_WhenNotOverridden::test 2313a0134836651e23c693505c3c000ad7ff24956b432d0761a5e8b77cb01642
// pending-parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.GetOSD_FromOSD_Roundtrip_Minimal::test 5e81b8a195f3e59bfe9e3ebf50c3291f7cbcd49f567ede7c1c8b726e0c6c9c19
// pending-parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.GetOSD_FromOSD_Roundtrip_Extras::test 63bae8bddacab170aec00c6d7a219f96fa794f489190b34cb426bbf9a3672af5
// pending-parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_MissingVolume_DoesNotThrow_AndUsesDefaults::test 03f9369aab6f6a71ee13982a7b6ba9780f86bcb5b6f081f9550f491b692f711c
// pending-parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_WrongTypes_ForTransforms_AreIgnored::test a23d4b1ab6d24c11c96862bc18ba57fccf1a8a1d690c90c5e8da3092973220f7
// pending-parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_PartialProfile_UsesDefaultsForMissingFields::test f3a3e506e3ef417f6323875db82179a4ffbdc1d4ee383417f2b0bd5afd0d0dba
// pending-parity-case: LibreMetaverse.Tests/ProductInfoRequestMessageTests.cs::ProductInfoRequestMessageTests.Deserialize_BareArray_ReadsSkuNameDescription::test 5d51fdbb1bec1ff74df59778d214413e78fb650679d36e7c44be92d1b2e3205f
// pending-parity-case: LibreMetaverse.Tests/ProductInfoRequestMessageTests.cs::ProductInfoRequestMessageTests.SerializeDeserialize_RoundTrips::test 5cb54bed4150cf13f815b223d6b7e35e7427004a12e98c7328c7674b6fe6309b
// pending-parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_HappyPath_ParsesAllowedMapAndBuildsExpectedQuery::test 2571d3a4100face21268d7ca2401cc8468e711c03c808704aacc7a50006125d5
// pending-parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NoExperienceIds_OmitsExperiencesParam::test ddbaa53aa8c63262c2241588b4281c42bbb66efdecac95e297deb0fb81797455
// pending-parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NoCapability_ReturnsNullWithoutRequest::test 5c6a6584fba8ddc66200367217f658d342fb2c26d6be9e49ccf3892bea420095
// pending-parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NonSuccessStatus_ReturnsNull::test 977fe55acd5aeb5735cc718f705a2742ae0dfab12dc1211c03a64cc6d1f3bbf5
// pending-parity-case: LibreMetaverse.Tests/RLV/Commands/AdjustHeightCommandTests.cs::AdjustHeightCommandTests.AdjustHeight::test fb3adb86988b10b2d3fe9223324f8a3c1ba7b8c2ea909ee6b57b38c590fed372
// pending-parity-case: LibreMetaverse.Tests/RLV/Commands/AdjustHeightCommandTests.cs::AdjustHeightCommandTests.AdjustHeight_WithDelta::test 1f37a125fd9df8585740d37c711888dfcd92460d86c19077f771d7f3fab54264
// pending-parity-case: LibreMetaverse.Tests/RLV/Commands/AttachAllCommandTests.cs::AttachAllCommandTests.AttachForce_Recursive::case:eee2b1b757fc196e 17a84542635b76f4f806126ab170d43441ad00b9b91650aa1164909de9692671
@@ -724,60 +682,3 @@
// pending-parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_WithNotificationRestrictions_SendsNotifications::test ba82b183dfdb01b36f9b45520e1808290c360f327d4afa833ba3b8c7a7cecffb
// pending-parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_WithComplexRestrictions_RemovesCorrectly::test 631de2d39fa56b29014fae4ce3b57c480f540ad41ce08a0037b7f8bbfcb3c502
// pending-parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_TriggersRestrictionUpdatedEvent::test 318f1f4207b04d01076d3498aceea781c4889b05cdbef199cdbea43dde355f4c
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_WeeklySchedule_ParsesDaysAndTime::test 0f64aab66c6a6d807b3d2bbc46b636a85900e4f76e35d5e3dd8cd0d8b295618b
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_DailySchedule_ReturnsAllDays::test cd966b6e3ba470c91ac958bf6c71aeb34f4dab060ad5334cb6060e1954b5c6d0
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_NoRestartConfigured_ReturnsNull::test 59a4b050a79659ccaf0fff13b750d634d803ed7b9e4940cbad676c1bdc04a5fc
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_NoCapability_ReturnsNullWithoutRequest::test e169e7846874ed4309a50cb25b03c2e722a8d758a787654a5906610ab3b655f1
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_Weekly_PostsUppercaseDaysAndSeconds::test 153fc0ac84af549c1381d0814a356bf2d9e0ead2c879e503981193366943546c
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_DailyIgnoresDays_DoesNotPostDaysKey::test b51a1272465c3316194542eb346b2aa15c43fdc8474163ddd851b3f2204d1c2b
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_EmptyDaysClearsSchedule::test 57ef3c0f8d9eeac58a7d1b3e7dbc44413016f39303d304cacd3071bd2c55f320
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_NoCapability_ReturnsFalseWithoutRequest::test be03507fe574b9f7c7174c39af44b7b693e1b7a496845db46451ef09bf9db206
// pending-parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_NonSuccessStatus_ReturnsFalse::test 16c8988d7aa15ea9f58deaa2e3f416b6115978e23073fd99749843f4f410dca4
// pending-parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_IgnoresPostBody_UsesSimConsoleResponseEvent::test df6eb48241f71c34074be66869c9ae1d5980186583259568728261b5eedd7c48
// pending-parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_NoResponseEvent_ReturnsNullAfterTimeout::test 2ac5a6c96f3acec341056387d727288fd3661565bd2a7c385078602efe8661da
// pending-parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SimConsoleResponseMessage_Deserialize_ReadsBodyField::test 4d367bc3fd012d1579847fd9837bf883c5ea95452776a086bbf856899d7c2099
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSimpleLocation_ParsesCorrectly::test 4ca1200596cd99959db205bd40050c9ed62ac68fcd58079d52882e3b24af3fdf
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSlurl_ParsesCorrectly::test b389e823a365884bb10813a587c5922416dc8d0737f36bfde99c56cc4e4accda
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSlurlWithTrailingSlash_ParsesCorrectly::test eb8513ad0d5dd9f67b537c1a844f738ef9363efdafd97aa31ec6fb697395c270
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLegacyUri_ParsesCorrectly::test d3e28edefeaa13a00d666cdc40c5ee60a24563462472786e0fa186262e8f6e73
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLocationWithDefaults_UsesDefaultCoordinates::test a89cceba86e86028cd70ee28b0f31a4c40c23491d582a0f70fc06b98bfeb3605
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLocationPartialCoordinates_UsesDefaults::test b6418ccbb2b00b741fcc92db7d0d8577e7334360934de7418f75ed08d7b3da24
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseAgentSlapp_ParsesCorrectly::test 77a274bb3b4bf189fab0ce68a729057d04376bf3d6d45d0a5443d0f301f5fbb8
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseGroupSlapp_ParsesCorrectly::test 5435e301f76398ff2f35e0b65a7055de6d42b1a4662936ee949005182511fcd6
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseTeleportSlapp_ParsesLocationData::test 68eea168b7480f125a4bff20b582af87c0f6d62502821bf57eb734b12f87eed5
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseWorldMapSlapp_ParsesLocationData::test ad7f5ce07a837501fa74dda643f1c0d278a2f51fe27330d3419c57683127bf9e
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLoginSlapp_ParsesQueryParameters::test a0622607f53b8c19348540e7b858d3c75be22b47b9afc92505121b3430c33550
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseObjectImSlapp_ParsesQueryParameters::test af9c2bdcfb212f08618719c85da41938207b6978fe2042a3c4451f7fd0075d6f
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSearchSlapp_ParsesCorrectly::test a77776e74cff175bde3e974344502369e679db15f45fe5c0d614c80732549765
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetRawLocation_ReturnsCorrectFormat::test 30dafa3972780bbbb07f1edaf55ed4bc3b01080e616588768ebef9df54678af3
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSlurl_ReturnsCorrectFormat::test 1e6b6fef442eab1ca6874df75f1a0ea380ec7f25974979d3cd878e10fc49fb40
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetStartLocationUri_ReturnsCorrectFormat::test c7cc2d49e52ed6ebb19e09607a2b191fb3835e8f3804c7b07c5c9e8fd3b9bcae
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetAgentUrl_GeneratesCorrectUrl::test b656f795ce39c9bc50df167e20e23b76b57929feaad1756d3190920f5410d0d4
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetAgentUrl_DefaultAction_UsesAbout::test 642b0543f72427cf646bcb8445b9acff4c1fdb0d26ef3f0dc6c10eaaed73028b
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetGroupUrl_GeneratesCorrectUrl::test 919cc7a17770fb65a83812836819a19d6dd4e630a848741d12df868da3d949d5
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetTeleportUrl_GeneratesCorrectUrl::test 3465edd1ecdd6e521c52687570d32e288bfa29eb6e47666305c64ee5322df159
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetTeleportUrl_WithDefaults_UsesDefaultCoordinates::test 823d524cc81d39753549583346953122c372f5575aa03d8bf101d1be3eae845d
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetWorldMapUrl_GeneratesCorrectUrl::test 7c19a936e0d641195f3806291b5b88970affa46f540ef950d55df709b52ad56b
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetObjectImUrl_GeneratesCorrectUrl::test edf724227cc86abc84d223637f34c607f9fc30f9e85b53a9ec8c8f2d2b7bcbf6
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetObjectImUrl_NotGroupOwned_OmitsGroupOwnedParameter::test 764978a618429216a0bfef4e72ac44a329d2b2d1c5b13af889db9cf86814cd99
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSearchUrl_GeneratesCorrectUrl::test aeb79bf4e5e51368b4520eec3cc5beeb767101b81f1eaa3f3ad118749dc5c2f0
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetLoginUrl_WithAllParameters_GeneratesCorrectUrl::test 0d9dc8d623e56ec1e8e0d414eb2c898bfd7ee37712972287834c2df6a99da17c
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetLoginUrl_WithNoParameters_GeneratesBaseUrl::test 1fcfa164861bcc115f2935f1c20c276dea05802cf35dbb55664ff1c36140c135
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSlappUrl_WithQueryParams_GeneratesCorrectUrl::test 37ca9ebe06ce42f5919d9b6ec30459d1455f78bde910a61d04187e74e7ce3d07
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseNull_ThrowsArgumentNullException::test 98c170ff5f03f843918c0d2aecddb23e2c2605e31100f5d3178ad7cb306eef5d
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseEmptyString_ThrowsArgumentException::test a5d89e3349033fa4efa1b00339556d811f175c52ca5b183a2d19b247a8eaeed8
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseUrlEncodedQueryParams_DecodesCorrectly::test 72ac64c89f3a08bbecfb80aaaf11f5ee59a383eeeb9578676af5b9d83f374cc4
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.RoundTripLocation_PreservesData::test 2cba174b3609af177295b469bad1641a98d8dc79d95aa4ef22403923edd4d092
// pending-parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseAllSlappCommands_RecognizesCorrectly::test abf53480da20d1ff57f5f06f1433c120ea84e0d6d5363cb33e4a275d76cfa306
// pending-parity-case: LibreMetaverse.Tests/StartAuctionTests.cs::StartAuctionTests.ViewerStartAuctionPacket_RoundTripsAgentAndParcelFields::test f37ef06cfba37754effe04a0bb0edfa159c761b811a05efd95157896756bef16
// pending-parity-case: LibreMetaverse.Tests/StartAuctionTests.cs::StartAuctionTests.StartAuction_DoesNotThrow::test deec194333b480dabb3f6e6d96b360a29d11a40ca1103117fc7ce5cdb80d6eb0
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:d3ad6116f2458248 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:c6f3ac57944a5314 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:d59eced1ded07f84 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:59df3d13bd106053 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_AllHighFaces_TextureIDs_AllPreserved::test 203ca43b9f1965e1fae7feb36e84eff1df9ac5968eb23753077816d16dd24228
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_LowAndHighFaces_BothPreserved::test dd520cac107415a186a44eb3a17bd000f933ec49b814a496e6788237fcfffb1e
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RGBA_IsPreserved::test db41d130d270d31f52f3928df88ccc052102673c1afbca03cc0648526fafbabb
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RepeatU_IsPreserved::test 58e80815263025ec54cd15e3c9a42771f5e55c81e9acb212a0f163faef9fd499
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RepeatV_IsPreserved::test fbeb1fdd584a26576b6f1ba653b2995d1a6d6bccc2c1dc51e46aa9f211b78724
// pending-parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_AllFaces_TextureIDs_AllPreserved::test 5e5a390af8deff77ef80f751735456541f24d7e27666557d50d99065cc3ba6a4

View File

@@ -0,0 +1,441 @@
// Exact Rust translation of LibreMetaverse.Tests/SlurlParserTests.cs.
// Source SHA-256: 3626ff6fae5a303e4037ac2b5742dfc7fa226e04a33b8d358ba355fccdd05611
use libremetaverse::{Error, SlappCommand, SlurlParser, ViewerUriType};
use libremetaverse_types::UUID;
use std::collections::HashMap;
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
fn parse(location: &str) -> SlurlParser {
SlurlParser::new(Some(location.into())).expect("SlurlParser constructor")
}
fn assert_location(parser: &SlurlParser, sim: &str, x: i32, y: i32, z: i32) {
assert_eq!(parser.sim(), sim);
assert_eq!(parser.x(), x);
assert_eq!(parser.y(), y);
assert_eq!(parser.z(), z);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSimpleLocation_ParsesCorrectly::test 4ca1200596cd99959db205bd40050c9ed62ac68fcd58079d52882e3b24af3fdf translated
#[test]
fn parse_simple_location_parses_correctly() {
let parser = parse("Hooper/128/128/32");
assert_location(&parser, "Hooper", 128, 128, 32);
assert_eq!(parser.uri_type(), ViewerUriType::Location);
assert!(parser.is_location());
assert!(!parser.is_application());
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSlurl_ParsesCorrectly::test b389e823a365884bb10813a587c5922416dc8d0737f36bfde99c56cc4e4accda translated
#[test]
fn parse_slurl_parses_correctly() {
let parser = parse("secondlife://Hooper/179/18/32");
assert_location(&parser, "Hooper", 179, 18, 32);
assert_eq!(parser.uri_type(), ViewerUriType::Location);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSlurlWithTrailingSlash_ParsesCorrectly::test eb8513ad0d5dd9f67b537c1a844f738ef9363efdafd97aa31ec6fb697395c270 translated
#[test]
fn parse_slurl_with_trailing_slash_parses_correctly() {
let parser = parse("secondlife://Hooper/179/18/32/");
assert_eq!(
(parser.sim(), parser.x(), parser.y(), parser.z()),
("Hooper".into(), 179, 18, 32)
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLegacyUri_ParsesCorrectly::test d3e28edefeaa13a00d666cdc40c5ee60a24563462472786e0fa186262e8f6e73 translated
#[test]
fn parse_legacy_uri_parses_correctly() {
let parser = parse("uri:Hooper&179&18&32");
assert_location(&parser, "Hooper", 179, 18, 32);
assert_eq!(parser.uri_type(), ViewerUriType::Location);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLocationWithDefaults_UsesDefaultCoordinates::test a89cceba86e86028cd70ee28b0f31a4c40c23491d582a0f70fc06b98bfeb3605 translated
#[test]
fn parse_location_with_defaults_uses_default_coordinates() {
let parser = parse("secondlife://Hooper");
assert_eq!(
(parser.sim(), parser.x(), parser.y(), parser.z()),
("Hooper".into(), 128, 128, 0)
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLocationPartialCoordinates_UsesDefaults::test b6418ccbb2b00b741fcc92db7d0d8577e7334360934de7418f75ed08d7b3da24 translated
#[test]
fn parse_location_partial_coordinates_uses_defaults() {
let parser = parse("secondlife://Hooper/100");
assert_eq!(
(parser.sim(), parser.x(), parser.y(), parser.z()),
("Hooper".into(), 100, 128, 0)
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseAgentSlapp_ParsesCorrectly::test 77a274bb3b4bf189fab0ce68a729057d04376bf3d6d45d0a5443d0f301f5fbb8 translated
#[test]
fn parse_agent_slapp_parses_correctly() {
let id = uuid();
let id_text = id.to_string();
let parser = parse(&format!("secondlife:///app/agent/{id_text}/about"));
assert_eq!(parser.uri_type(), ViewerUriType::Application);
assert_eq!(parser.command(), SlappCommand::Agent);
assert_eq!(parser.command_path(), format!("agent/{id_text}/about"));
assert!(parser.is_application());
assert!(!parser.is_location());
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseGroupSlapp_ParsesCorrectly::test 5435e301f76398ff2f35e0b65a7055de6d42b1a4662936ee949005182511fcd6 translated
#[test]
fn parse_group_slapp_parses_correctly() {
let id = uuid();
let id_text = id.to_string();
let parser = parse(&format!("secondlife:///app/group/{id_text}/about"));
assert_eq!(parser.uri_type(), ViewerUriType::Application);
assert_eq!(parser.command(), SlappCommand::Group);
assert_eq!(parser.command_path(), format!("group/{id_text}/about"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseTeleportSlapp_ParsesLocationData::test 68eea168b7480f125a4bff20b582af87c0f6d62502821bf57eb734b12f87eed5 translated
#[test]
fn parse_teleport_slapp_parses_location_data() {
let parser = parse("secondlife:///app/teleport/Hooper/100/200/50");
assert_eq!(parser.uri_type(), ViewerUriType::Application);
assert_eq!(parser.command(), SlappCommand::Teleport);
assert_location(&parser, "Hooper", 100, 200, 50);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseWorldMapSlapp_ParsesLocationData::test ad7f5ce07a837501fa74dda643f1c0d278a2f51fe27330d3419c57683127bf9e translated
#[test]
fn parse_world_map_slapp_parses_location_data() {
let parser = parse("secondlife:///app/worldmap/Sandbox/128/128/0");
assert_eq!(parser.uri_type(), ViewerUriType::Application);
assert_eq!(parser.command(), SlappCommand::WorldMap);
assert_location(&parser, "Sandbox", 128, 128, 0);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseLoginSlapp_ParsesQueryParameters::test a0622607f53b8c19348540e7b858d3c75be22b47b9afc92505121b3430c33550 translated
#[test]
fn parse_login_slapp_parses_query_parameters() {
let parser = parse("secondlife:///app/login?last=Resident&session=abc123&location=home");
assert_eq!(parser.uri_type(), ViewerUriType::Application);
assert_eq!(parser.command(), SlappCommand::Login);
let query = parser.query_parameters();
assert_eq!(query.get("last").map(String::as_str), Some("Resident"));
assert_eq!(query.get("session").map(String::as_str), Some("abc123"));
assert_eq!(query.get("location").map(String::as_str), Some("home"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseObjectImSlapp_ParsesQueryParameters::test af9c2bdcfb212f08618719c85da41938207b6978fe2042a3c4451f7fd0075d6f translated
#[test]
fn parse_object_im_slapp_parses_query_parameters() {
let object = uuid();
let owner = uuid();
let object_text = object.to_string();
let owner_text = owner.to_string();
let parser = parse(&format!(
"secondlife:///app/objectim/{object_text}?name=TestObject&owner={owner_text}&groupowned=true&slurl=Hooper/128/128/25"
));
let query = parser.query_parameters();
assert_eq!(parser.command(), SlappCommand::ObjectIm);
assert_eq!(query.get("name").map(String::as_str), Some("TestObject"));
assert_eq!(query.get("owner"), Some(&owner.to_string()));
assert_eq!(query.get("groupowned").map(String::as_str), Some("true"));
assert_eq!(
query.get("slurl").map(String::as_str),
Some("Hooper/128/128/25")
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseSearchSlapp_ParsesCorrectly::test a77776e74cff175bde3e974344502369e679db15f45fe5c0d614c80732549765 translated
#[test]
fn parse_search_slapp_parses_correctly() {
let parser = parse("secondlife:///app/search/places/Sandbox");
assert_eq!(parser.command(), SlappCommand::Search);
assert!(parser.command_path().contains("places"));
assert!(parser.command_path().contains("Sandbox"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetRawLocation_ReturnsCorrectFormat::test 30dafa3972780bbbb07f1edaf55ed4bc3b01080e616588768ebef9df54678af3 translated
#[test]
fn get_raw_location_returns_correct_format() {
assert_eq!(
parse("Hooper/100/200/50").get_raw_location().unwrap(),
"Hooper/100/200/50"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSlurl_ReturnsCorrectFormat::test 1e6b6fef442eab1ca6874df75f1a0ea380ec7f25974979d3cd878e10fc49fb40 translated
#[test]
fn get_slurl_returns_correct_format() {
assert_eq!(
parse("Hooper/100/200/50").get_slurl().unwrap(),
"secondlife://Hooper/100/200/50/"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetStartLocationUri_ReturnsCorrectFormat::test c7cc2d49e52ed6ebb19e09607a2b191fb3835e8f3804c7b07c5c9e8fd3b9bcae translated
#[test]
fn get_start_location_uri_returns_correct_format() {
assert_eq!(
parse("Hooper/100/200/50").get_start_location_uri().unwrap(),
"uri:Hooper&100&200&50"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetAgentUrl_GeneratesCorrectUrl::test b656f795ce39c9bc50df167e20e23b76b57929feaad1756d3190920f5410d0d4 translated
#[test]
fn get_agent_url_generates_correct_url() {
let id = uuid();
let id_text = id.to_string();
assert_eq!(
SlurlParser::get_agent_url(id, Some("inspect".into())).unwrap(),
format!("secondlife:///app/agent/{id_text}/inspect")
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetAgentUrl_DefaultAction_UsesAbout::test 642b0543f72427cf646bcb8445b9acff4c1fdb0d26ef3f0dc6c10eaaed73028b translated
#[test]
fn get_agent_url_default_action_uses_about() {
assert!(
SlurlParser::get_agent_url(uuid(), None)
.unwrap()
.contains("/about")
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetGroupUrl_GeneratesCorrectUrl::test 919cc7a17770fb65a83812836819a19d6dd4e630a848741d12df868da3d949d5 translated
#[test]
fn get_group_url_generates_correct_url() {
let id = uuid();
let id_text = id.to_string();
assert_eq!(
SlurlParser::get_group_url(id, Some("inspect".into())).unwrap(),
format!("secondlife:///app/group/{id_text}/inspect")
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetTeleportUrl_GeneratesCorrectUrl::test 3465edd1ecdd6e521c52687570d32e288bfa29eb6e47666305c64ee5322df159 translated
#[test]
fn get_teleport_url_generates_correct_url() {
assert_eq!(
SlurlParser::get_teleport_url("Hooper".into(), Some(100), Some(200), Some(50)).unwrap(),
"secondlife:///app/teleport/Hooper/100/200/50"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetTeleportUrl_WithDefaults_UsesDefaultCoordinates::test 823d524cc81d39753549583346953122c372f5575aa03d8bf101d1be3eae845d translated
#[test]
fn get_teleport_url_with_defaults_uses_default_coordinates() {
assert_eq!(
SlurlParser::get_teleport_url("Hooper".into(), None, None, None).unwrap(),
"secondlife:///app/teleport/Hooper/128/128/0"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetWorldMapUrl_GeneratesCorrectUrl::test 7c19a936e0d641195f3806291b5b88970affa46f540ef950d55df709b52ad56b translated
#[test]
fn get_world_map_url_generates_correct_url() {
assert_eq!(
SlurlParser::get_world_map_url("Sandbox".into(), Some(64), Some(64), Some(100)).unwrap(),
"secondlife:///app/worldmap/Sandbox/64/64/100"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetObjectImUrl_GeneratesCorrectUrl::test edf724227cc86abc84d223637f34c607f9fc30f9e85b53a9ec8c8f2d2b7bcbf6 translated
#[test]
fn get_object_im_url_generates_correct_url() {
let object = uuid();
let owner = uuid();
let object_text = object.to_string();
let owner_text = owner.to_string();
let url = SlurlParser::get_object_im_url(
object,
"Test Object".into(),
owner,
true,
"Hooper/128/128/25".into(),
)
.unwrap();
assert!(url.contains(&format!("objectim/{object_text}")));
assert!(url.contains("name=Test%20Object"));
assert!(url.contains(&format!("owner={owner_text}")));
assert!(url.contains("groupowned=true"));
assert!(url.contains("slurl=Hooper"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetObjectImUrl_NotGroupOwned_OmitsGroupOwnedParameter::test 764978a618429216a0bfef4e72ac44a329d2b2d1c5b13af889db9cf86814cd99 translated
#[test]
fn get_object_im_url_not_group_owned_omits_group_owned_parameter() {
let url = SlurlParser::get_object_im_url(
uuid(),
"Test Object".into(),
uuid(),
false,
"Hooper/128/128/25".into(),
)
.unwrap();
assert!(!url.contains("groupowned"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSearchUrl_GeneratesCorrectUrl::test aeb79bf4e5e51368b4520eec3cc5beeb767101b81f1eaa3f3ad118749dc5c2f0 translated
#[test]
fn get_search_url_generates_correct_url() {
assert_eq!(
SlurlParser::get_search_url("places".into(), "sandbox island".into()).unwrap(),
"secondlife:///app/search/places/sandbox%20island"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetLoginUrl_WithAllParameters_GeneratesCorrectUrl::test 0d9dc8d623e56ec1e8e0d414eb2c898bfd7ee37712972287834c2df6a99da17c translated
#[test]
fn get_login_url_with_all_parameters_generates_correct_url() {
let url = SlurlParser::get_login_url(
Some("Resident".into()),
Some("session123".into()),
Some("home".into()),
)
.unwrap();
assert!(url.contains("login?"));
assert!(url.contains("last=Resident"));
assert!(url.contains("session=session123"));
assert!(url.contains("location=home"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetLoginUrl_WithNoParameters_GeneratesBaseUrl::test 1fcfa164861bcc115f2935f1c20c276dea05802cf35dbb55664ff1c36140c135 translated
#[test]
fn get_login_url_with_no_parameters_generates_base_url() {
assert_eq!(
SlurlParser::get_login_url(None, None, None).unwrap(),
"secondlife:///app/login"
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.GetSlappUrl_WithQueryParams_GeneratesCorrectUrl::test 37ca9ebe06ce42f5919d9b6ec30459d1455f78bde910a61d04187e74e7ce3d07 translated
#[test]
fn get_slapp_url_with_query_params_generates_correct_url() {
let query = HashMap::from([
("test".into(), "value".into()),
("foo".into(), "bar".into()),
]);
let url =
SlurlParser::get_slapp_url(SlappCommand::Help, Some("topic".into()), Some(query)).unwrap();
assert!(url.contains("help/topic"));
assert!(url.contains("test=value"));
assert!(url.contains("foo=bar"));
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseNull_ThrowsArgumentNullException::test 98c170ff5f03f843918c0d2aecddb23e2c2605e31100f5d3178ad7cb306eef5d translated
#[test]
fn parse_null_throws_argument_null() {
match SlurlParser::new(None) {
Err(error) => assert_eq!(error, Error::ArgumentNull),
Ok(_) => panic!("expected ArgumentNull"),
}
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseEmptyString_ThrowsArgumentException::test a5d89e3349033fa4efa1b00339556d811f175c52ca5b183a2d19b247a8eaeed8 translated
#[test]
fn parse_empty_string_throws_argument() {
match SlurlParser::new(Some(String::new())) {
Err(error) => assert_eq!(error, Error::Argument),
Ok(_) => panic!("expected Argument"),
}
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseUrlEncodedQueryParams_DecodesCorrectly::test 72ac64c89f3a08bbecfb80aaaf11f5ee59a383eeeb9578676af5b9d83f374cc4 translated
#[test]
fn parse_url_encoded_query_params_decodes_correctly() {
assert!(
parse("secondlife:///app/search/places/sandbox%20island")
.command_path()
.contains("sandbox%20island")
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.RoundTripLocation_PreservesData::test 2cba174b3609af177295b469bad1641a98d8dc79d95aa4ef22403923edd4d092 translated
#[test]
fn round_trip_location_preserves_data() {
let original = parse("Hooper/100/200/50");
let parsed = parse(&original.get_slurl().unwrap());
assert_location(
&parsed,
&original.sim(),
original.x(),
original.y(),
original.z(),
);
}
// parity-case: LibreMetaverse.Tests/SlurlParserTests.cs::SlurlParserTests.ParseAllSlappCommands_RecognizesCorrectly::test abf53480da20d1ff57f5f06f1433c120ea84e0d6d5363cb33e4a275d76cfa306 translated
#[test]
fn parse_all_slapp_commands_recognizes_correctly() {
let commands = [
("secondlife:///app/agent/uuid/about", SlappCommand::Agent),
(
"secondlife:///app/appearance/show",
SlappCommand::Appearance,
),
("secondlife:///app/balance/request", SlappCommand::Balance),
("secondlife:///app/chat/1/hello", SlappCommand::Chat),
(
"secondlife:///app/classified/uuid/about",
SlappCommand::Classified,
),
("secondlife:///app/event/uuid/about", SlappCommand::Event),
(
"secondlife:///app/experience/uuid/profile",
SlappCommand::Experience,
),
("secondlife:///app/group/uuid/about", SlappCommand::Group),
("secondlife:///app/help/search", SlappCommand::Help),
(
"secondlife:///app/inventory/uuid/select",
SlappCommand::Inventory,
),
(
"secondlife:///app/keybinding/jump",
SlappCommand::Keybinding,
),
("secondlife:///app/login", SlappCommand::Login),
(
"secondlife:///app/maptrackavatar/uuid",
SlappCommand::MapTrackAvatar,
),
("secondlife:///app/objectim/uuid", SlappCommand::ObjectIm),
(
"secondlife:///app/openfloater/preferences",
SlappCommand::OpenFloater,
),
("secondlife:///app/parcel/uuid/about", SlappCommand::Parcel),
("secondlife:///app/region/uuid/about", SlappCommand::Region),
("secondlife:///app/search/all/test", SlappCommand::Search),
(
"secondlife:///app/sharewithavatar/uuid",
SlappCommand::ShareWithAvatar,
),
(
"secondlife:///app/teleport/Hooper/128/128/0",
SlappCommand::Teleport,
),
(
"secondlife:///app/voicecallavatar/uuid",
SlappCommand::VoiceCallAvatar,
),
(
"secondlife:///app/wear_folder/?folder_id=uuid",
SlappCommand::WearFolder,
),
(
"secondlife:///app/worldmap/Hooper/128/128/0",
SlappCommand::WorldMap,
),
];
for (url, expected) in commands {
assert_eq!(parse(url).command(), expected, "failed to parse {url}");
}
}

View File

@@ -0,0 +1,489 @@
// Exact Rust translations of world capability tests owned by milestone issue #23.
// Source SHA-256: f83be0075683edda91f8e345661b2f753d81c134585f38ddd52b8b9b9e406089
// Source SHA-256: 79483c67d93295f09d281ec577f642d4967e281f88e531b2b43ee14c1444a440
// Source SHA-256: 1ef332ad434b9bdc468282eae99150393950db352a98f5f822517fa48021222f
// Source SHA-256: e49de5d4d588aa944bc27eeecd9921891b851eaecb76270abcec91f649579f7b
use libremetaverse::messages::linden::{EnvironmentData, SimConsoleResponseMessage};
use libremetaverse::{
GridClient, HttpCapsClient, RegionRestartDays, RegionRestartSchedule, Simulator,
};
use libremetaverse_compat_tests::block_on;
use libremetaverse_structured_data::{OSD, OSDParser};
use libremetaverse_types::UUID;
use libremetaverse_types::compat::{HttpMessageHandler, HttpRequest, HttpResponse, Uri};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
const CAP: &str = "http://test.invalid/capability";
const SEED: &str = "http://test.invalid/seed";
#[derive(Clone)]
struct Recording(Arc<Mutex<Vec<HttpRequest>>>);
impl Recording {
fn requests(&self) -> Vec<HttpRequest> {
self.0.lock().unwrap().clone()
}
}
fn response(status: u16, content_type: &str, body: &str) -> HttpResponse {
HttpResponse {
status_code: status,
headers: BTreeMap::new(),
content_type: Some(content_type.into()),
body: body.as_bytes().to_vec(),
}
}
fn client_with_cap(
cap_name: &str,
expose_capability: bool,
cap_response: HttpResponse,
) -> (GridClient, Recording) {
let requests = Arc::new(Mutex::new(Vec::new()));
let captured = Arc::clone(&requests);
let cap_reply = Mutex::new(Some(cap_response));
let seed = if expose_capability {
format!("<llsd><map><key>{cap_name}</key><uri>{CAP}</uri></map></llsd>")
} else {
"<llsd><map></map></llsd>".into()
};
let handler = HttpMessageHandler::new(move |request, _| {
let reply = if request.uri.0 == SEED {
response(200, "application/llsd+xml", &seed)
} else {
captured.lock().unwrap().push(request);
cap_reply
.lock()
.unwrap()
.take()
.unwrap_or_else(|| response(404, "application/octet-stream", ""))
};
async move { reply }
});
let mut client = GridClient::new().expect("GridClient constructor");
client.set_http_caps_client(HttpCapsClient::new(handler.clone()).unwrap());
let mut simulator_client = GridClient::new().expect("simulator GridClient constructor");
simulator_client.set_http_caps_client(HttpCapsClient::new(handler).unwrap());
let simulator = Simulator::new(
simulator_client,
"127.0.0.1:13".parse().unwrap(),
0,
None,
None,
)
.unwrap();
simulator
.set_seed_caps(Some(Uri(SEED.into())), Some(true))
.unwrap();
let mut network = client.network();
network.set_current_sim(Some(simulator));
client.set_network(network);
(client, Recording(requests))
}
fn only_request(recording: &Recording) -> HttpRequest {
let requests = recording.requests();
assert_eq!(requests.len(), 1);
requests.into_iter().next().unwrap()
}
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
// parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.GetParcelEnvironment_UsesParcelidQueryParameter::test 5c2f2b0fa2a86955975c621b2415a92ff3da283215e669956e14bf5ee2a4c1f4 translated
#[test]
fn get_parcel_environment_uses_parcelid_query_parameter() {
let (client, recording) = client_with_cap(
"ExtEnvironment",
true,
response(
200,
"application/llsd+xml",
"<llsd><map><key>success</key><boolean>true</boolean></map></llsd>",
),
);
block_on(client.environment().get_parcel_environment(42, None)).unwrap();
let request = only_request(&recording);
assert_eq!(request.method, "GET");
assert!(request.uri.0.contains("parcelid=42"));
assert!(!request.uri.0.contains("parcel_id"));
}
// parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetParcelEnvironment_SendsParcelidInQueryStringNotBody::test f633e2d78e4c6a0fff42917ccfe8de68e4f6133a11e7eef7f9b757c3d1622f0f translated
#[test]
fn set_parcel_environment_sends_parcelid_in_query_not_body() {
let (client, recording) = client_with_cap(
"ExtEnvironment",
true,
response(
200,
"application/llsd+xml",
"<llsd><map><key>success</key><boolean>true</boolean></map></llsd>",
),
);
let mut environment = EnvironmentData::new().expect("EnvironmentData constructor");
environment.set_day_length(7200);
block_on(
client
.environment()
.set_parcel_environment(7, environment, None),
)
.unwrap();
let request = only_request(&recording);
assert_eq!(request.method, "POST");
assert!(request.uri.0.contains("parcelid=7"));
let OSD::Map(body) = OSDParser::deserialize_with_bytes(request.body).unwrap() else {
panic!("ExtEnvironment POST body is not an LLSD map")
};
assert!(!body.contains_key("parcel_id"));
let Some(OSD::Map(environment)) = body.get("environment") else {
panic!("environment map missing")
};
assert_eq!(
environment.get("day_length").unwrap().as_integer().unwrap(),
7200
);
assert!(!environment.contains_key("sky_track"));
}
// parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.SetRegionEnvironment_OmitsQueryStringEntirely::test bade6e06765506ad7e500ac3e5c9f168605ce25fef86647819d285a68b39f109 translated
#[test]
fn set_region_environment_omits_query_string_entirely() {
let (client, recording) = client_with_cap(
"ExtEnvironment",
true,
response(
200,
"application/llsd+xml",
"<llsd><map><key>success</key><boolean>true</boolean></map></llsd>",
),
);
let environment = EnvironmentData::new().expect("EnvironmentData constructor");
block_on(
client
.environment()
.set_region_environment_with_environment_data_cancellation_token(environment, None),
)
.unwrap();
let request = only_request(&recording);
assert_eq!(request.method, "POST");
assert_eq!(request.uri.0, CAP);
}
// parity-case: LibreMetaverse.Tests/EnvironmentManagerTests.cs::EnvironmentManagerTests.ResetParcelEnvironment_SendsHttpDeleteNotPost::test 4360439192c137f5d7a2ceb83b8664f3917b4b261798050cb855d596f483f082 translated
#[test]
fn reset_parcel_environment_sends_delete_not_post() {
let (client, recording) = client_with_cap(
"ExtEnvironment",
true,
response(
200,
"application/llsd+xml",
"<llsd><map><key>success</key><boolean>true</boolean></map></llsd>",
),
);
assert!(block_on(client.environment().reset_parcel_environment(3, None)).unwrap());
let request = only_request(&recording);
assert_eq!(request.method, "DELETE");
assert!(request.uri.0.contains("parcelid=3"));
}
// parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_HappyPath_ParsesAllowedMapAndBuildsExpectedQuery::test 2571d3a4100face21268d7ca2401cc8468e711c03c808704aacc7a50006125d5 translated
#[test]
fn query_experiences_on_parcel_happy_path() {
let first = uuid();
let second = uuid();
let first_text = first.to_string();
let second_text = second.to_string();
let body = format!(r#"{{"experiences":{{"{first_text}":true,"{second_text}":false}}}}"#);
let (mut client, recording) = client_with_cap(
"ExperienceQuery",
true,
response(200, "application/json", &body),
);
let result = block_on(client.self_().query_experiences_on_parcel(
42,
Box::new([first, second].into_iter()),
None,
))
.unwrap()
.expect("experience result");
assert_eq!(result.get(&first), Some(&true));
assert_eq!(result.get(&second), Some(&false));
let request = only_request(&recording);
assert!(request.uri.0.contains("parcelid=42"));
assert!(
request
.uri
.0
.contains(&format!("experiences={first_text},{second_text}"))
);
}
// parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NoExperienceIds_OmitsExperiencesParam::test ddbaa53aa8c63262c2241588b4281c42bbb66efdecac95e297deb0fb81797455 translated
#[test]
fn query_experiences_on_parcel_no_ids_omits_parameter() {
let (mut client, recording) = client_with_cap(
"ExperienceQuery",
true,
response(200, "application/json", r#"{"experiences":{}}"#),
);
let result = block_on(client.self_().query_experiences_on_parcel(
7,
Box::new(std::iter::empty()),
None,
))
.unwrap()
.expect("experience result");
assert!(result.is_empty());
let request = only_request(&recording);
assert!(request.uri.0.contains("parcelid=7"));
assert!(!request.uri.0.contains("experiences"));
}
// parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NoCapability_ReturnsNullWithoutRequest::test 5c6a6584fba8ddc66200367217f658d342fb2c26d6be9e49ccf3892bea420095 translated
#[test]
fn query_experiences_on_parcel_no_capability_returns_none_without_request() {
let (mut client, recording) = client_with_cap(
"ExperienceQuery",
false,
response(404, "application/json", ""),
);
let result = block_on(client.self_().query_experiences_on_parcel(
1,
Box::new([uuid()].into_iter()),
None,
))
.unwrap();
assert!(result.is_none());
assert!(recording.requests().is_empty());
}
// parity-case: LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs::QueryExperiencesOnParcelTests.QueryExperiencesOnParcelAsync_NonSuccessStatus_ReturnsNull::test 977fe55acd5aeb5735cc718f705a2742ae0dfab12dc1211c03a64cc6d1f3bbf5 translated
#[test]
fn query_experiences_on_parcel_non_success_returns_none() {
let (mut client, _) = client_with_cap(
"ExperienceQuery",
true,
response(500, "application/json", ""),
);
assert!(
block_on(client.self_().query_experiences_on_parcel(
1,
Box::new([uuid()].into_iter()),
None
))
.unwrap()
.is_none()
);
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_WeeklySchedule_ParsesDaysAndTime::test 0f64aab66c6a6d807b3d2bbc46b636a85900e4f76e35d5e3dd8cd0d8b295618b translated
#[test]
fn get_region_restart_schedule_weekly_parses_days_and_time() {
let (client, _) = client_with_cap(
"RegionSchedule",
true,
response(
200,
"application/json",
r#"{"restart":{"type":"W","days":"TR","time":7200}}"#,
),
);
let schedule = block_on(client.estate().get_region_restart_schedule(None))
.unwrap()
.expect("restart schedule");
assert!(!schedule.is_daily());
assert_eq!(
schedule.days(),
RegionRestartDays(RegionRestartDays::TUESDAY.0 | RegionRestartDays::THURSDAY.0)
);
assert_eq!(schedule.time(), Duration::from_secs(2 * 60 * 60));
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_DailySchedule_ReturnsAllDays::test cd966b6e3ba470c91ac958bf6c71aeb34f4dab060ad5334cb6060e1954b5c6d0 translated
#[test]
fn get_region_restart_schedule_daily_returns_all_days() {
let (client, _) = client_with_cap(
"RegionSchedule",
true,
response(
200,
"application/json",
r#"{"restart":{"type":"D","time":3600}}"#,
),
);
let schedule = block_on(client.estate().get_region_restart_schedule(None))
.unwrap()
.expect("restart schedule");
assert!(schedule.is_daily());
assert_eq!(schedule.days(), RegionRestartDays::ALL);
assert_eq!(schedule.time(), Duration::from_secs(60 * 60));
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_NoRestartConfigured_ReturnsNull::test 59a4b050a79659ccaf0fff13b750d634d803ed7b9e4940cbad676c1bdc04a5fc translated
#[test]
fn get_region_restart_schedule_no_restart_returns_none() {
let (client, _) = client_with_cap(
"RegionSchedule",
true,
response(200, "application/json", "{}"),
);
assert!(
block_on(client.estate().get_region_restart_schedule(None))
.unwrap()
.is_none()
);
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.GetRegionRestartScheduleAsync_NoCapability_ReturnsNullWithoutRequest::test e169e7846874ed4309a50cb25b03c2e722a8d758a787654a5906610ab3b655f1 translated
#[test]
fn get_region_restart_schedule_no_capability_returns_none_without_request() {
let (client, recording) = client_with_cap(
"RegionSchedule",
false,
response(404, "application/json", ""),
);
assert!(
block_on(client.estate().get_region_restart_schedule(None))
.unwrap()
.is_none()
);
assert!(recording.requests().is_empty());
}
fn schedule(daily: bool, days: RegionRestartDays, time: Duration) -> RegionRestartSchedule {
let mut schedule = RegionRestartSchedule::new().expect("RegionRestartSchedule constructor");
schedule.set_is_daily(daily);
schedule.set_days(days);
schedule.set_time(time);
schedule
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_Weekly_PostsUppercaseDaysAndSeconds::test 153fc0ac84af549c1381d0814a356bf2d9e0ead2c879e503981193366943546c translated
#[test]
fn set_region_restart_schedule_weekly_posts_uppercase_days_and_seconds() {
let (client, recording) = client_with_cap(
"RegionSchedule",
true,
response(200, "application/json", ""),
);
let days = RegionRestartDays(
RegionRestartDays::MONDAY.0 | RegionRestartDays::WEDNESDAY.0 | RegionRestartDays::FRIDAY.0,
);
assert!(
block_on(
client.estate().set_region_restart_schedule(
schedule(false, days, Duration::from_secs(5400)),
None
)
)
.unwrap()
);
let body = String::from_utf8(only_request(&recording).body).unwrap();
assert!(body.contains("<key>type</key>"));
assert!(body.contains('W'));
assert!(body.contains("MWF"));
assert!(body.contains("5400"));
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_DailyIgnoresDays_DoesNotPostDaysKey::test b51a1272465c3316194542eb346b2aa15c43fdc8474163ddd851b3f2204d1c2b translated
#[test]
fn set_region_restart_schedule_daily_omits_days() {
let (client, recording) = client_with_cap(
"RegionSchedule",
true,
response(200, "application/json", ""),
);
assert!(
block_on(client.estate().set_region_restart_schedule(
schedule(
true,
RegionRestartDays::NONE,
Duration::from_secs(3 * 60 * 60)
),
None
))
.unwrap()
);
assert!(
!String::from_utf8(only_request(&recording).body)
.unwrap()
.contains("<key>days</key>")
);
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_EmptyDaysClearsSchedule::test 57ef3c0f8d9eeac58a7d1b3e7dbc44413016f39303d304cacd3071bd2c55f320 translated
#[test]
fn set_region_restart_schedule_empty_days_clears_schedule() {
let (client, recording) = client_with_cap(
"RegionSchedule",
true,
response(200, "application/json", ""),
);
assert!(
block_on(client.estate().set_region_restart_schedule(
schedule(false, RegionRestartDays::NONE, Duration::ZERO),
None
))
.unwrap()
);
let body = String::from_utf8(only_request(&recording).body).unwrap();
assert!(body.contains("<key>days</key>"));
assert!(body.contains("<string />") || body.contains("<string></string>"));
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_NoCapability_ReturnsFalseWithoutRequest::test be03507fe574b9f7c7174c39af44b7b693e1b7a496845db46451ef09bf9db206 translated
#[test]
fn set_region_restart_schedule_no_capability_returns_false_without_request() {
let (client, recording) = client_with_cap(
"RegionSchedule",
false,
response(404, "application/json", ""),
);
assert!(
!block_on(client.estate().set_region_restart_schedule(
schedule(true, RegionRestartDays::NONE, Duration::ZERO),
None
))
.unwrap()
);
assert!(recording.requests().is_empty());
}
// parity-case: LibreMetaverse.Tests/RegionScheduleTests.cs::RegionScheduleTests.SetRegionRestartScheduleAsync_NonSuccessStatus_ReturnsFalse::test 16c8988d7aa15ea9f58deaa2e3f416b6115978e23073fd99749843f4f410dca4 translated
#[test]
fn set_region_restart_schedule_non_success_returns_false() {
let (client, _) = client_with_cap(
"RegionSchedule",
true,
response(500, "application/json", ""),
);
assert!(
!block_on(client.estate().set_region_restart_schedule(
schedule(true, RegionRestartDays::NONE, Duration::ZERO),
None
))
.unwrap()
);
}
// parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SimConsoleResponseMessage_Deserialize_ReadsBodyField::test 4d367bc3fd012d1579847fd9837bf883c5ea95452776a086bbf856899d7c2099 translated
#[test]
fn sim_console_response_message_deserialize_reads_body_field() {
let map = libremetaverse_structured_data::OSDMap::new_with_dictionary(
std::collections::HashMap::from([(
"body".into(),
OSD::String("console output text".into()),
)]),
)
.unwrap();
let mut message =
SimConsoleResponseMessage::new().expect("SimConsoleResponseMessage constructor");
message.deserialize(map).unwrap();
assert_eq!(message.body, "console output text");
}

View File

@@ -0,0 +1,23 @@
// Exact Rust translations of the world-manager cases in
// LibreMetaverse.Tests/ConstructorNullArgumentTests.cs.
// Source SHA-256: 28deea0bf4d9353a2424842c6184abab8fe82746ceb12e093912728a247f21bc
use libremetaverse::{AgentManager, Error, GridManager, ObjectManager};
// parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.GridManager_Null_Throws::test e972a95f334b0f46948cda34c27124c28c44faf4372d0e598d7dc60050b49be4 translated
#[test]
fn grid_manager_null_throws() {
assert!(matches!(GridManager::new(None), Err(Error::ArgumentNull)));
}
// parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.ObjectManager_Null_Throws::test 8a11feba0edfb35e5dd99f681182e8f5a5cf8a3f738211b45a683a8a2ad45ddb translated
#[test]
fn object_manager_null_throws() {
assert!(matches!(ObjectManager::new(None), Err(Error::ArgumentNull)));
}
// parity-case: LibreMetaverse.Tests/ConstructorNullArgumentTests.cs::ConstructorNullArgumentTests.AgentManager_Null_Throws::test d86e41b6cdd03063685aa330c31b9a274c1d400b9a461abf3065fe185fe0bbf0 translated
#[test]
fn agent_manager_null_throws() {
assert!(matches!(AgentManager::new(None), Err(Error::ArgumentNull)));
}

View File

@@ -0,0 +1,81 @@
// Exact Rust translation of LibreMetaverse.Tests/EstateToolsTests.cs.
// Source SHA-256: 2f1867304e21e72d20fbeed98e7705528797947eb3115f84f80aa6adc2f3f88e
use libremetaverse::{GridClient, NetworkManager};
use libremetaverse_compat_tests::{block_on, live_grid_credentials};
use libremetaverse_types::UUID;
use std::sync::mpsc;
use std::time::Duration;
struct LiveEstate {
client: GridClient,
}
impl LiveEstate {
fn login() -> Self {
let mut client = GridClient::new().expect("GridClient constructor");
client.settings().timing().login_timeout = 30_000;
let network = client.network();
let (first, last, password, login_url) = live_grid_credentials();
let start = NetworkManager::start_location("Hooper".into(), 179, 18, 32)
.expect("NetworkManager StartLocation");
let mut login = network
.default_login_params(
first,
last,
password,
"Unit Test Framework".into(),
"admin@radegast.life".into(),
)
.expect("NetworkManager DefaultLoginParams");
login.start = start;
login.uri = login_url;
let logged_in = block_on(network.login_with_login_params_cancellation_token(login, None))
.expect("NetworkManager LoginAsync");
assert!(
logged_in,
"client failed to login: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
let current = network.current_sim().expect("CurrentSim after login");
if !current.name.is_empty() && !current.name.eq_ignore_ascii_case("Hooper") {
eprintln!(
"logged in to {} instead of Hooper; covenant request remains read-only",
current.name
);
}
Self { client }
}
}
impl Drop for LiveEstate {
fn drop(&mut self) {
let _ = self.client.network().logout_with_method();
let _ = self.client.dispose_with_method();
}
}
// parity-case: LibreMetaverse.Tests/EstateToolsTests.cs::EstateToolsTests.RequestConvenant::test d8d752dd6e53477e4c66bcb5d0e3ec1021a9b3ceda1f689f59b5e606e1713729 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; performs only a reversible read-only covenant request"
)]
fn request_covenant() {
let live = LiveEstate::login();
let estate = live.client.estate();
let (sender, receiver) = mpsc::sync_channel(1);
let _subscription = estate.subscribe_estate_covenant_reply(std::sync::Arc::new(move |reply| {
let _ = sender.send((reply.estate_name(), reply.covenant_id()));
}));
estate
.request_covenant()
.expect("EstateTools RequestCovenant");
let Ok((name, covenant)) = receiver.recv_timeout(Duration::from_secs(10)) else {
eprintln!("timeout waiting for estate covenant reply after 10 seconds");
return;
};
assert!(!name.is_empty());
assert_ne!(covenant, UUID::zero());
}

View File

@@ -0,0 +1,150 @@
// Exact Rust translations of LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs and
// LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs public message cases.
// Source SHA-256: b023ec7ac43124fcd47b15474a9d66d1fca522d1166dc0d5c21b8c82189051f2
// Source SHA-256: 7b67b72c2ad652626126d0e4eda7e8c26fc94c28f0f915238b2925f80a3d4af2
use libremetaverse::messages::linden::{
ExperienceListMessage, ExperiencePreferencesMessage, RegionExperiencesMessage,
};
use libremetaverse_structured_data::{OSD, OSDMap};
use libremetaverse_types::UUID;
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
fn map(entries: impl IntoIterator<Item = (&'static str, OSD)>) -> OSDMap {
OSDMap::new_with_dictionary(
entries
.into_iter()
.map(|(key, value)| (key.to_owned(), value))
.collect(),
)
.expect("OSDMap dictionary constructor")
}
fn ids(values: impl IntoIterator<Item = UUID>) -> OSD {
OSD::Array(values.into_iter().map(OSD::UUID).collect())
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Deserialize_ReadsExperienceIdsKey::test aa1f7d09f6c79118ea2e60621d57bd63632085ce81bc979e42294aca53d4f0d9 translated
#[test]
fn experience_list_deserialize_reads_experience_ids_key() {
let id = uuid();
let mut message = ExperienceListMessage::new().expect("ExperienceListMessage constructor");
message
.deserialize(map([("experience_ids", ids([id]))]))
.unwrap();
assert_eq!(message.experience_i_ds, vec![id]);
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Deserialize_IgnoresLegacyExperienceKeysKey::test cd1d372b9a976c04dee11da1ade182e4f9239e1f7633e788347c6d5f255835ec translated
#[test]
fn experience_list_deserialize_ignores_legacy_experience_keys_key() {
let mut message = ExperienceListMessage::new().expect("ExperienceListMessage constructor");
message
.deserialize(map([("experience_keys", ids([uuid()]))]))
.unwrap();
assert!(message.experience_i_ds.is_empty());
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.ExperienceListMessage_Serialize_WritesExperienceIdsKey::test 3fa4cb5d598940f693285bcc64179a4dea927a3643a76a061a933a265488d4be translated
#[test]
fn experience_list_serialize_writes_experience_ids_key() {
let mut message = ExperienceListMessage::new().expect("ExperienceListMessage constructor");
message.experience_i_ds.push(uuid());
let encoded = message.serialize().unwrap();
assert!(encoded.contains_key("experience_ids".into()).unwrap());
assert!(!encoded.contains_key("experience_keys".into()).unwrap());
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Deserialize_ReadsBlockedTrustedAllowedAndDefault::test 61df3b0c60d32b6c9bb843eb7a1a11fe8c0b1fd59c7a09853ad1b0e1d93cd4fd translated
#[test]
fn region_experiences_deserialize_reads_all_lists_and_default() {
let blocked = uuid();
let trusted = uuid();
let allowed = uuid();
let default_experience = uuid();
let mut message =
RegionExperiencesMessage::new().expect("RegionExperiencesMessage constructor");
message
.deserialize(map([
("blocked", ids([blocked])),
("trusted", ids([trusted])),
("allowed", ids([allowed])),
("default", OSD::UUID(default_experience)),
]))
.unwrap();
assert_eq!(message.blocked, vec![blocked]);
assert_eq!(message.trusted, vec![trusted]);
assert_eq!(message.allowed, vec![allowed]);
assert_eq!(message.default, default_experience);
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Deserialize_IgnoresLegacyContribKey::test 851d037919003ebb414105e57bf37f73ba37dff8f733c36183c447f1349f3316 translated
#[test]
fn region_experiences_deserialize_ignores_legacy_contrib_key() {
let mut message =
RegionExperiencesMessage::new().expect("RegionExperiencesMessage constructor");
message
.deserialize(map([
("blocked", OSD::Array(Vec::new())),
("trusted", OSD::Array(Vec::new())),
("contrib", ids([uuid()])),
]))
.unwrap();
assert!(message.allowed.is_empty());
}
// parity-case: LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs::ExperienceListAndRegionMessageTests.RegionExperiencesMessage_Serialize_WritesAllowedKeyNotContrib::test 53c3a7d6a200816615a9efaa222ccf294e3a8334d320812c6ec56a0a3e7e05bb translated
#[test]
fn region_experiences_serialize_writes_allowed_not_contrib() {
let mut message =
RegionExperiencesMessage::new().expect("RegionExperiencesMessage constructor");
message.allowed.push(uuid());
let encoded = message.serialize().unwrap();
assert!(encoded.contains_key("allowed".into()).unwrap());
assert!(!encoded.contains_key("contrib".into()).unwrap());
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Deserialize_ReadsAllowedListFromExperiencesKey::test 9bab78dbe142a3da6ad21a278f25d7da90776fce3861c7d602203c1e1842c9af translated
#[test]
fn experience_preferences_deserialize_reads_experiences_and_blocked() {
let allowed = uuid();
let blocked = uuid();
let mut message =
ExperiencePreferencesMessage::new().expect("ExperiencePreferencesMessage constructor");
message
.deserialize(map([
("experiences", ids([allowed])),
("blocked", ids([blocked])),
]))
.unwrap();
assert_eq!(message.allowed, vec![allowed]);
assert_eq!(message.blocked, vec![blocked]);
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Deserialize_IgnoresLegacyAllowedKey::test ec6d5e7c81214ea4e3d99740fdd33961578f895a90a42082067ca9819d93d2b0 translated
#[test]
fn experience_preferences_deserialize_ignores_legacy_allowed_key() {
let mut message =
ExperiencePreferencesMessage::new().expect("ExperiencePreferencesMessage constructor");
message
.deserialize(map([
("allowed", ids([uuid()])),
("blocked", OSD::Array(Vec::new())),
]))
.unwrap();
assert!(message.allowed.is_empty());
}
// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.Serialize_WritesAllowedListUnderExperiencesKey::test 45a0bffe39ab7bf0734ec5fce4435dbdda7ae94cf4e933210669e91dc45b1baf translated
#[test]
fn experience_preferences_serialize_writes_experiences_not_allowed() {
let mut message =
ExperiencePreferencesMessage::new().expect("ExperiencePreferencesMessage constructor");
message.allowed.push(uuid());
let encoded = message.serialize().unwrap();
assert!(encoded.contains_key("experiences".into()).unwrap());
assert!(!encoded.contains_key("allowed".into()).unwrap());
}

View File

@@ -0,0 +1,652 @@
// Exact Rust translations of object and texture tests owned by milestone issue #23.
// Source SHA-256: 1a9d986c5e2d7526e95a7bb5b870ebee13ddf9f4e8c3fd69c34b7a7f22375916
// Source SHA-256: b444ada9b4d09944dbdf7668c0c8105de66fb590bee1d6d1f284b30700ee5284
// Source SHA-256: f9a33efa0e88e4a7d86bf7beda56c0f45757ff66f0cdc161da384ad5a31467a6
#![allow(clippy::float_cmp)]
use libremetaverse::{
Bumpiness, Helpers, MappingType, Primitive, PrimitiveConstructionData, PrimitiveFlexibleData,
PrimitiveLightData, PrimitiveLightImage, PrimitiveObjectProperties, PrimitiveSculptData,
PrimitiveTextureEntry, Shininess,
};
use libremetaverse_structured_data::OSD;
use libremetaverse_types::{
Color4, HoleType, Material, PCode, PathCurve, PrimFlags, ProfileCurve, Quaternion, SculptType,
UUID, Vector3,
};
use std::collections::HashMap;
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
fn round_trip(entry: &PrimitiveTextureEntry) -> PrimitiveTextureEntry {
let bytes = entry.get_bytes().expect("TextureEntry GetBytes");
let length = i32::try_from(bytes.len()).expect("TextureEntry length fits Int32");
PrimitiveTextureEntry::new_with_bytes_int32_int32(bytes, 0, length)
.expect("TextureEntry byte constructor")
}
fn face(entry: &PrimitiveTextureEntry, index: usize) -> &libremetaverse::PrimitiveTextureEntryFace {
entry.face_textures[index].as_ref().expect("per-face entry")
}
fn assert_vector(actual: Vector3, expected: Vector3) {
assert_eq!(
(actual.x, actual.y, actual.z),
(expected.x, expected.y, expected.z)
);
}
fn assert_quaternion(actual: Quaternion, expected: Quaternion) {
assert_eq!(
(actual.x, actual.y, actual.z, actual.w),
(expected.x, expected.y, expected.z, expected.w)
);
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathBegin::test 5c8e14e1d08a91a4e7c3e252c932b8fe15cf6b5231cc9fe5bb7ef2ae6e2c8299 translated
#[test]
fn path_begin() {
for value in 0_u16..u16::from(u8::MAX) {
let unpacked = Primitive::unpack_begin_cut(value).unwrap();
assert_eq!(
Primitive::pack_begin_cut(unpacked).unwrap(),
value,
"started with {value}, float value was {unpacked}"
);
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathEnd::test 693bfe29f448e433705d65b8b6907f1b270fd636f914e18b76d7a0047dbea506 translated
#[test]
fn path_end() {
for value in 0_u16..u16::from(u8::MAX) {
let unpacked = Primitive::unpack_end_cut(value).unwrap();
assert_eq!(
Primitive::pack_end_cut(unpacked).unwrap(),
value,
"started with {value}, float value was {unpacked}"
);
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathRevolutions::test c3f61c80a204e608976cb44a0c10b9651496074c25f254eec8621db65dbbbe37 translated
#[test]
fn path_revolutions() {
for value in 0_u8..u8::MAX {
let unpacked = Primitive::unpack_path_revolutions(value).unwrap();
assert_eq!(
Primitive::pack_path_revolutions(unpacked).unwrap(),
value,
"started with {value}, float value was {unpacked}"
);
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathScale::test b92e4ab375fcc788dea701d8579268f39b4f3b1d51d4eed91458547350405d61 translated
#[test]
fn path_scale() {
for value in 0_u8..u8::MAX {
let unpacked = Primitive::unpack_path_scale(value).unwrap();
assert_eq!(
Primitive::pack_path_scale(unpacked).unwrap(),
value,
"started with {value}, float value was {unpacked}"
);
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.PathTaper::test 93a0adbc0630de3c9fbea6f8080d74ef6e3180e3813f00460bce5d52ef2f12b7 translated
#[test]
fn path_taper() {
for value in i8::MIN..i8::MAX {
let unpacked = Primitive::unpack_path_taper(value).unwrap();
assert_eq!(
Primitive::pack_path_taper(unpacked).unwrap(),
value,
"started with {value}, float value was {unpacked}"
);
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntryOffsets::test 720ea1a9477d333b14786bc14e9abca24858e63acae94f4c6055d81f14ff033a translated
#[test]
fn texture_entry_offsets() {
let mut value = -1.0_f32;
while value <= 1.0 {
value = (value * 1000.0).round() / 1000.0;
let packed = Helpers::te_offset_short(value).unwrap();
let unpacked = Helpers::te_offset_float(packed.to_le_bytes().to_vec(), 0).unwrap();
let rounded = (unpacked * 1000.0).round() / 1000.0;
assert!(
(rounded - value).abs() < f32::from_bits(1),
"{rounded} is not equal to {value}"
);
value += 0.001;
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry::test 0670a9a8dcc4001e726df699ef8001604956b6674028a192502b92b725858990 translated
#[test]
fn texture_entry() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
let face = entry.create_face(0).unwrap();
face.set_bump(Bumpiness::Concrete);
face.set_fullbright(true);
face.set_media_flags(true);
face.set_offset_u(0.5);
face.set_offset_v(-0.5);
face.set_repeat_u(3.0);
face.set_repeat_v(4.0);
face.set_rgba(Color4 {
r: 0.0,
g: 0.25,
b: 0.75,
a: 1.0,
});
face.set_rotation(1.5);
face.set_shiny(Shininess::Medium);
face.set_tex_map_type(MappingType::Planar);
face.set_texture_id(uuid());
let first = entry.get_bytes().unwrap();
let length = i32::try_from(first.len()).expect("TextureEntry length fits Int32");
let decoded =
PrimitiveTextureEntry::new_with_bytes_int32_int32(first.clone(), 0, length).unwrap();
let second = decoded.get_bytes().unwrap();
assert_eq!(first.len(), second.len());
for (index, (actual, expected)) in first.iter().zip(&second).enumerate() {
assert_eq!(actual, expected, "byte {index} is not equal");
}
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_DefaultFace_Roundtrips::test 8db56b448e53e6bec5540a5a4721dc75f85a933c6c89e166f7df7baf8e44aab3 translated
#[test]
fn render_material_id_default_face_round_trips() {
let material = uuid();
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry
.default_texture
.as_mut()
.unwrap()
.set_render_material_id(material);
assert_eq!(
round_trip(&entry)
.default_texture
.as_ref()
.unwrap()
.render_material_id(),
material
);
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_PerFace_Roundtrips::test 2897366ecaa41f07b4db6ebf3e80a7ebe56480f1c44f31cedea2d88a62214dc6 translated
#[test]
fn render_material_id_per_face_round_trips() {
let material = uuid();
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry
.create_face(0)
.unwrap()
.set_render_material_id(material);
let decoded = round_trip(&entry);
assert_eq!(face(&decoded, 0).render_material_id(), material);
assert_eq!(
decoded
.default_texture
.as_ref()
.unwrap()
.render_material_id(),
UUID::zero()
);
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_MultipleFaces_Roundtrips::test f3d8c103e4b3d43a8e3bea1356ba13808633fdb2ea06583234551f9059b2a241 translated
#[test]
fn render_material_id_multiple_faces_round_trip() {
let default_material = uuid();
let first = uuid();
let second = uuid();
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry
.default_texture
.as_mut()
.unwrap()
.set_render_material_id(default_material);
entry.create_face(0).unwrap().set_render_material_id(first);
entry.create_face(1).unwrap().set_render_material_id(second);
let decoded = round_trip(&entry);
assert_eq!(
decoded
.default_texture
.as_ref()
.unwrap()
.render_material_id(),
default_material
);
assert_eq!(face(&decoded, 0).render_material_id(), first);
assert_eq!(face(&decoded, 1).render_material_id(), second);
}
// parity-case: LibreMetaverse.Tests/PrimObjectTests.cs::PrimObjectTests.TextureEntry_RenderMaterialID_InheritsFromDefault_WhenNotOverridden::test 2313a0134836651e23c693505c3c000ad7ff24956b432d0761a5e8b77cb01642 translated
#[test]
fn render_material_id_inherits_from_default_when_not_overridden() {
let material = uuid();
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry
.default_texture
.as_mut()
.unwrap()
.set_render_material_id(material);
let decoded = round_trip(&entry);
assert_eq!(
decoded
.default_texture
.as_ref()
.unwrap()
.render_material_id(),
material
);
if let Some(face) = decoded.get_face(0).unwrap() {
assert_eq!(face.render_material_id(), material);
}
}
fn high_face_round_trip(index: u32) -> bool {
let texture = uuid();
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry.create_face(index).unwrap().set_texture_id(texture);
let decoded = round_trip(&entry);
face(&decoded, index as usize).texture_id() == texture
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:d3ad6116f2458248 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55 translated
#[test]
fn high_face_32_texture_id_is_preserved() {
assert!(high_face_round_trip(32));
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:c6f3ac57944a5314 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55 translated
#[test]
fn high_face_33_texture_id_is_preserved() {
assert!(high_face_round_trip(33));
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:d59eced1ded07f84 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55 translated
#[test]
fn high_face_40_texture_id_is_preserved() {
assert!(high_face_round_trip(40));
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_HighFace_TextureID_IsPreserved::case:59df3d13bd106053 5f215cd1c50fa8871967653b4667f848c14ff24a3b71906e51aac13c56de1f55 translated
#[test]
fn high_face_44_texture_id_is_preserved() {
assert!(high_face_round_trip(44));
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_AllHighFaces_TextureIDs_AllPreserved::test 203ca43b9f1965e1fae7feb36e84eff1df9ac5968eb23753077816d16dd24228 translated
#[test]
fn all_high_face_texture_ids_are_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
let expected: Vec<_> = (32..PrimitiveTextureEntry::MAX_FACES)
.map(|index| {
let texture = uuid();
entry
.create_face(u32::try_from(index).expect("face index fits UInt32"))
.unwrap()
.set_texture_id(texture);
texture
})
.collect();
let decoded = round_trip(&entry);
for (offset, texture) in expected.into_iter().enumerate() {
assert_eq!(face(&decoded, offset + 32).texture_id(), texture);
}
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_LowAndHighFaces_BothPreserved::test dd520cac107415a186a44eb3a17bd000f933ec49b814a496e6788237fcfffb1e translated
#[test]
fn low_and_high_faces_are_both_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
let expected = [(0, uuid()), (15, uuid()), (32, uuid()), (44, uuid())];
for (index, texture) in expected {
entry.create_face(index).unwrap().set_texture_id(texture);
}
let decoded = round_trip(&entry);
for (index, texture) in expected {
assert_eq!(
face(&decoded, index as usize).texture_id(),
texture,
"face {index}"
);
}
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RGBA_IsPreserved::test db41d130d270d31f52f3928df88ccc052102673c1afbca03cc0648526fafbabb translated
#[test]
fn face_32_rgba_is_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry.default_texture.as_mut().unwrap().set_rgba(Color4 {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
});
entry.create_face(32).unwrap().set_rgba(Color4 {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
});
let color = face(&round_trip(&entry), 32).rgba();
assert!((color.r - 1.0).abs() <= 0.01);
assert!(color.g.abs() <= 0.01);
assert!(color.b.abs() <= 0.01);
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RepeatU_IsPreserved::test 58e80815263025ec54cd15e3c9a42771f5e55c81e9acb212a0f163faef9fd499 translated
#[test]
fn face_32_repeat_u_is_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry.create_face(32).unwrap().set_repeat_u(4.0);
assert!((face(&round_trip(&entry), 32).repeat_u() - 4.0).abs() <= 0.01);
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_Face32_RepeatV_IsPreserved::test fbeb1fdd584a26576b6f1ba653b2995d1a6d6bccc2c1dc51e46aa9f211b78724 translated
#[test]
fn face_32_repeat_v_is_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
entry.create_face(32).unwrap().set_repeat_v(4.0);
assert!((face(&round_trip(&entry), 32).repeat_v() - 4.0).abs() <= 0.01);
}
// parity-case: LibreMetaverse.Tests/TextureEntryTests.cs::TextureEntryTests.RoundTrip_AllFaces_TextureIDs_AllPreserved::test 5e5a390af8deff77ef80f751735456541f24d7e27666557d50d99065cc3ba6a4 translated
#[test]
fn all_face_texture_ids_are_preserved() {
let mut entry = PrimitiveTextureEntry::new_with_uuid(uuid()).unwrap();
let expected: Vec<_> = (0..PrimitiveTextureEntry::MAX_FACES)
.map(|index| {
let texture = uuid();
entry
.create_face(u32::try_from(index).expect("face index fits UInt32"))
.unwrap()
.set_texture_id(texture);
texture
})
.collect();
let decoded = round_trip(&entry);
for (index, texture) in expected.into_iter().enumerate() {
assert_eq!(face(&decoded, index).texture_id(), texture, "face {index}");
}
}
// parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.GetOSD_FromOSD_Roundtrip_Minimal::test 5e81b8a195f3e59bfe9e3ebf50c3291f7cbcd49f567ede7c1c8b726e0c6c9c19 translated
#[test]
fn primitive_osd_round_trip_minimal() {
let mut primitive = Primitive::new_with_constructor().unwrap();
primitive.id = uuid();
primitive.local_id = 12_345;
primitive.parent_id = 54_321;
primitive.flags =
PrimFlags(PrimFlags::PHANTOM.0 | PrimFlags::PHYSICS.0 | PrimFlags::CAST_SHADOWS.0);
primitive.position = Vector3 {
x: 1.1,
y: 2.2,
z: 3.3,
};
primitive.rotation = Quaternion {
x: 0.1,
y: 0.2,
z: 0.3,
w: 0.9,
};
primitive.scale = Vector3 {
x: 0.5,
y: 0.6,
z: 0.7,
};
let mut data = PrimitiveConstructionData::new_with_constructor().unwrap();
data.material = Material::Wood;
data.p_code = PCode::Prim;
data.state = 7;
data.path_curve = PathCurve::Circle;
data.path_scale_x = 1.2;
data.path_scale_y = 0.8;
data.profile_begin = 0.0;
data.profile_end = 1.0;
data.profile_hollow = 0.1;
data.set_profile_curve_with_property(ProfileCurve::Square);
data.set_profile_hole(HoleType(1));
primitive.prim_data = data;
let mut properties = PrimitiveObjectProperties::new().unwrap();
properties.name = "TestPrim".into();
properties.description = "A primitive used in unit tests".into();
primitive.properties = Some(properties);
let decoded = Primitive::from_osd(primitive.get_osd().unwrap()).unwrap();
assert_eq!(decoded.id, primitive.id);
assert_eq!(decoded.local_id, primitive.local_id);
assert_eq!(decoded.parent_id, primitive.parent_id);
assert_ne!(decoded.flags.0 & PrimFlags::PHANTOM.0, 0);
assert_ne!(decoded.flags.0 & PrimFlags::PHYSICS.0, 0);
assert_ne!(decoded.flags.0 & PrimFlags::CAST_SHADOWS.0, 0);
assert_vector(decoded.position, primitive.position);
assert_quaternion(decoded.rotation, primitive.rotation);
assert_vector(decoded.scale, primitive.scale);
assert_eq!(decoded.prim_data.material, primitive.prim_data.material);
assert_eq!(decoded.prim_data.p_code, primitive.prim_data.p_code);
assert_eq!(decoded.prim_data.state, primitive.prim_data.state);
assert_eq!(decoded.prim_data.path_curve, primitive.prim_data.path_curve);
assert_eq!(
decoded.prim_data.path_scale_x,
primitive.prim_data.path_scale_x
);
assert_eq!(
decoded.prim_data.path_scale_y,
primitive.prim_data.path_scale_y
);
assert_eq!(
decoded.prim_data.profile_begin,
primitive.prim_data.profile_begin
);
assert_eq!(
decoded.prim_data.profile_end,
primitive.prim_data.profile_end
);
assert_eq!(
decoded.prim_data.profile_hollow,
primitive.prim_data.profile_hollow
);
assert_eq!(
decoded.prim_data.profile_curve_with_property(),
primitive.prim_data.profile_curve_with_property()
);
assert_eq!(
decoded.prim_data.profile_hole(),
primitive.prim_data.profile_hole()
);
let decoded_properties = decoded.properties.as_ref().expect("properties");
let original_properties = primitive.properties.as_ref().unwrap();
assert_eq!(decoded_properties.name, original_properties.name);
assert_eq!(
decoded_properties.description,
original_properties.description
);
}
// parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.GetOSD_FromOSD_Roundtrip_Extras::test 63bae8bddacab170aec00c6d7a219f96fa794f489190b34cb426bbf9a3672af5 translated
#[test]
fn primitive_osd_round_trip_extras() {
let mut primitive = Primitive::new_with_constructor().unwrap();
primitive.id = uuid();
let mut flexible = PrimitiveFlexibleData::new_with_constructor().unwrap();
flexible.softness = 3;
flexible.tension = 1.5;
flexible.drag = 0.7;
flexible.gravity = -9.8;
flexible.wind = 0.2;
flexible.force = Vector3 {
x: 0.1,
y: 0.2,
z: 0.3,
};
primitive.flexible = Some(flexible);
let mut light = PrimitiveLightData::new_with_constructor().unwrap();
light.color = Color4 {
r: 0.2,
g: 0.4,
b: 0.6,
a: 1.0,
};
light.intensity = 0.8;
light.radius = 10.0;
light.cutoff = 0.5;
light.falloff = 2.0;
primitive.light = Some(light);
let mut image = PrimitiveLightImage::new_with_constructor().unwrap();
image.light_texture = uuid();
image.params = Vector3 {
x: 1.0,
y: 2.0,
z: 3.0,
};
primitive.light_map = Some(image);
let mut sculpt = PrimitiveSculptData::new_with_constructor().unwrap();
sculpt.sculpt_texture = uuid();
sculpt.set_type_(SculptType::Mesh);
primitive.sculpt = Some(sculpt);
let decoded = Primitive::from_osd(primitive.get_osd().unwrap()).unwrap();
let (actual, expected) = (
decoded.flexible.as_ref().unwrap(),
primitive.flexible.as_ref().unwrap(),
);
assert_eq!(
(
actual.softness,
actual.tension,
actual.drag,
actual.gravity,
actual.wind
),
(
expected.softness,
expected.tension,
expected.drag,
expected.gravity,
expected.wind
)
);
assert_vector(actual.force, expected.force);
let (actual, expected) = (
decoded.light.as_ref().unwrap(),
primitive.light.as_ref().unwrap(),
);
assert_eq!(
(
actual.color.r,
actual.color.g,
actual.color.b,
actual.intensity,
actual.radius,
actual.cutoff,
actual.falloff
),
(
expected.color.r,
expected.color.g,
expected.color.b,
expected.intensity,
expected.radius,
expected.cutoff,
expected.falloff
)
);
let (actual, expected) = (
decoded.light_map.as_ref().unwrap(),
primitive.light_map.as_ref().unwrap(),
);
assert_eq!(actual.light_texture, expected.light_texture);
assert_vector(actual.params, expected.params);
let (actual, expected) = (
decoded.sculpt.as_ref().unwrap(),
primitive.sculpt.as_ref().unwrap(),
);
assert_eq!(actual.sculpt_texture, expected.sculpt_texture);
assert_eq!(actual.type_(), expected.type_());
}
// parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_MissingVolume_DoesNotThrow_AndUsesDefaults::test 03f9369aab6f6a71ee13982a7b6ba9780f86bcb5b6f081f9550f491b692f711c translated
#[test]
fn primitive_from_osd_missing_volume_uses_defaults() {
let id = uuid();
let primitive = Primitive::from_osd(OSD::Map(HashMap::from([
("id".into(), OSD::UUID(id)),
("localid".into(), OSD::Integer(99)),
("parentid".into(), OSD::Integer(100)),
])))
.unwrap();
assert_eq!(primitive.id, id);
assert_eq!(primitive.local_id, 99);
assert_eq!(primitive.parent_id, 100);
assert_eq!(primitive.prim_data.path_curve, PathCurve::Line);
assert_eq!(
primitive.prim_data.profile_curve_with_property(),
ProfileCurve::Circle
);
assert_eq!(primitive.prim_data.profile_end, 1.0);
}
// parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_WrongTypes_ForTransforms_AreIgnored::test a23d4b1ab6d24c11c96862bc18ba57fccf1a8a1d690c90c5e8da3092973220f7 translated
#[test]
fn primitive_from_osd_wrong_transform_types_are_ignored() {
let primitive = Primitive::from_osd(OSD::Map(HashMap::from([
("position".into(), OSD::String("not-a-vector".into())),
("rotation".into(), OSD::Integer(123)),
("scale".into(), OSD::String("also-not-a-vector".into())),
])))
.unwrap();
assert_vector(
primitive.position,
Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
);
assert_quaternion(
primitive.rotation,
Quaternion {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
},
);
assert_vector(
primitive.scale,
Vector3 {
x: 1.0,
y: 1.0,
z: 1.0,
},
);
}
// parity-case: LibreMetaverse.Tests/PrimitiveTests.cs::PrimitiveTests.FromOSD_PartialProfile_UsesDefaultsForMissingFields::test f3a3e506e3ef417f6323875db82179a4ffbdc1d4ee383417f2b0bd5afd0d0dba translated
#[test]
fn primitive_from_osd_partial_profile_uses_defaults() {
let profile = OSD::Map(HashMap::from([(
"curve".into(),
OSD::Integer(ProfileCurve::EqualTriangle as i32),
)]));
let volume = OSD::Map(HashMap::from([("profile".into(), profile)]));
let primitive =
Primitive::from_osd(OSD::Map(HashMap::from([("volume".into(), volume)]))).unwrap();
assert_eq!(
primitive.prim_data.profile_curve_with_property(),
ProfileCurve::EqualTriangle
);
assert_eq!(primitive.prim_data.profile_begin, 0.0);
assert_eq!(primitive.prim_data.profile_end, 1.0);
assert_eq!(primitive.prim_data.profile_hollow, 0.0);
}

View File

@@ -0,0 +1,60 @@
// Exact Rust translation of LibreMetaverse.Tests/StartAuctionTests.cs.
// Source SHA-256: 7087a3957277dc76a018ff3478e69c556f33a890ada9ad2c8c53f3fc34ec7375
use libremetaverse::packets::{
Packet, PacketType, ViewerStartAuctionPacket, ViewerStartAuctionPacketAgentDataBlock,
ViewerStartAuctionPacketParcelDataBlock,
};
use libremetaverse::{GridClient, Simulator};
use libremetaverse_types::UUID;
fn uuid() -> UUID {
UUID::random().expect("UUID Random")
}
// parity-case: LibreMetaverse.Tests/StartAuctionTests.cs::StartAuctionTests.ViewerStartAuctionPacket_RoundTripsAgentAndParcelFields::test f37ef06cfba37754effe04a0bb0edfa159c761b811a05efd95157896756bef16 translated
#[test]
fn viewer_start_auction_packet_round_trips_agent_and_parcel_fields() {
let agent = uuid();
let session = uuid();
let snapshot = uuid();
let packet = ViewerStartAuctionPacket {
agent_data: ViewerStartAuctionPacketAgentDataBlock {
agent_id: agent,
session_id: session,
},
parcel_data: ViewerStartAuctionPacketParcelDataBlock {
local_id: 12_345,
snapshot_id: snapshot,
},
};
let bytes = packet.to_bytes_with_method().unwrap();
let mut end = i32::try_from(bytes.len()).expect("packet length fits Int32") - 1;
let built =
Packet::build_packet_with_bytes_int32_bytes(bytes.clone(), &mut end, Vec::new()).unwrap();
assert_eq!(built.type_, PacketType::ViewerStartAuction);
let mut position = 0;
let parsed = ViewerStartAuctionPacket::new_with_bytes_int32(bytes, &mut position).unwrap();
assert_eq!(parsed.agent_data.agent_id, agent);
assert_eq!(parsed.agent_data.session_id, session);
assert_eq!(parsed.parcel_data.local_id, 12_345);
assert_eq!(parsed.parcel_data.snapshot_id, snapshot);
}
// parity-case: LibreMetaverse.Tests/StartAuctionTests.cs::StartAuctionTests.StartAuction_DoesNotThrow::test deec194333b480dabb3f6e6d96b360a29d11a40ca1103117fc7ce5cdb80d6eb0 translated
#[test]
fn start_auction_does_not_throw() {
let client = GridClient::new().expect("GridClient constructor");
let simulator = Simulator::new(
GridClient::new().expect("simulator GridClient constructor"),
"127.0.0.1:13".parse().unwrap(),
0,
None,
None,
)
.expect("Simulator constructor");
client
.parcels()
.start_auction(simulator, 1, uuid())
.unwrap();
}