From ce28e056c20bf2723f565bbf464b87781ec248a2 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:42 -0500 Subject: Second Life viewer sources 1.20.2 --- linden/indra/test/files.lst | 2 + linden/indra/test/inventory.cpp | 4 +- linden/indra/test/io.cpp | 2 +- linden/indra/test/llcontrol_tut.cpp | 147 +++ linden/indra/test/llmessageconfig_tut.cpp | 24 +- linden/indra/test/llmessagetemplateparser_tut.cpp | 2 +- linden/indra/test/llnamevalue_tut.cpp | 550 +---------- linden/indra/test/llsdmessagereader_tut.cpp | 2 +- linden/indra/test/llstreamtools_tut.cpp | 42 +- linden/indra/test/lltemplatemessagebuilder_tut.cpp | 2 +- linden/indra/test/lltut.h | 10 + linden/indra/test/mass_properties_tut.cpp | 1008 ++++++++++++++++++++ linden/indra/test/math.cpp | 442 ++++++++- linden/indra/test/message_tut.cpp | 2 +- linden/indra/test/prim_linkability_tut.cpp | 490 ++++++++++ linden/indra/test/test.vcproj | 19 +- linden/indra/test/test_vc8.vcproj | 6 +- linden/indra/test/test_vc9.vcproj | 2 +- 18 files changed, 2198 insertions(+), 558 deletions(-) create mode 100644 linden/indra/test/llcontrol_tut.cpp create mode 100644 linden/indra/test/mass_properties_tut.cpp create mode 100644 linden/indra/test/prim_linkability_tut.cpp (limited to 'linden/indra/test') diff --git a/linden/indra/test/files.lst b/linden/indra/test/files.lst index d923d8f..b335dbf 100644 --- a/linden/indra/test/files.lst +++ b/linden/indra/test/files.lst @@ -5,6 +5,7 @@ test/llapp_tut.cpp test/llbase64_tut.cpp test/llblowfish_tut.cpp test/llbuffer_tut.cpp +test/llcontrol_tut.cpp test/lldate_tut.cpp test/llerror_tut.cpp test/llhost_tut.cpp @@ -38,6 +39,7 @@ test/lluuidhashmap_tut.cpp test/llxfer_tut.cpp test/math.cpp test/message_tut.cpp +test/prim_linkability_tut.cpp test/reflection_tut.cpp test/test.cpp test/v2math_tut.cpp diff --git a/linden/indra/test/inventory.cpp b/linden/indra/test/inventory.cpp index 7c81dbe..3899dfc 100644 --- a/linden/indra/test/inventory.cpp +++ b/linden/indra/test/inventory.cpp @@ -453,9 +453,7 @@ namespace tut ensure_equals("9.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); ensure_equals("10.name::getName() failed", src1->getName(), src2->getName()); ensure_equals("11.description::getDescription() failed", src1->getDescription(), src2->getDescription()); - - - skip_fail("12.creation::getCreationDate()"); + //skip_fail("12.creation::getCreationDate()"); ensure_equals("12.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); } diff --git a/linden/indra/test/io.cpp b/linden/indra/test/io.cpp index fe8c19d..408b7c8 100644 --- a/linden/indra/test/io.cpp +++ b/linden/indra/test/io.cpp @@ -1151,7 +1151,7 @@ namespace tut chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, 0.2); + mPump->addChain(chain, 0.2f); chain.clear(); // pump for a bit and make sure all 3 chains are running diff --git a/linden/indra/test/llcontrol_tut.cpp b/linden/indra/test/llcontrol_tut.cpp new file mode 100644 index 0000000..aabe205 --- /dev/null +++ b/linden/indra/test/llcontrol_tut.cpp @@ -0,0 +1,147 @@ +/** + * @file llcontrol_tut.cpp + * @date February 2008 + * @brief control group unit tests + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include +#include "lltut.h" + +#include "llcontrol.h" +#include "llsdserialize.h" + +namespace tut +{ + + struct control_group + { + LLControlGroup* mCG; + LLString mTestConfigDir; + LLString mTestConfigFile; + static bool mListenerFired; + control_group() + { + mCG = new LLControlGroup; + LLUUID random; + random.generate(); + // generate temp dir + std::ostringstream oStr; + oStr << "/tmp/llcontrol-test-" << random << "/"; + mTestConfigDir = oStr.str(); + mTestConfigFile = mTestConfigDir + "settings.xml"; + LLFile::mkdir(mTestConfigDir.c_str()); + LLSD config; + config["TestSetting"]["Comment"] = "Dummy setting used for testing"; + config["TestSetting"]["Persist"] = 1; + config["TestSetting"]["Type"] = "U32"; + config["TestSetting"]["Value"] = 12; + writeSettingsFile(config); + } + ~control_group() + { + //Remove test files + delete mCG; + } + void writeSettingsFile(const LLSD& config) + { + llofstream file(mTestConfigFile.c_str()); + if (file.is_open()) + { + LLSDSerialize::toPrettyXML(config, file); + } + file.close(); + } + static bool handleListenerTest(const LLSD& newvalue) + { + control_group::mListenerFired = true; + return true; + } + }; + + bool control_group::mListenerFired = false; + + typedef test_group control_group_test; + typedef control_group_test::object control_group_t; + control_group_test tut_control_group("control_group"); + + //load settings from files - LLSD + template<> template<> + void control_group_t::test<1>() + { + int results = mCG->loadFromFile(mTestConfigFile.c_str()); + ensure("number of settings", (results == 1)); + ensure("value of setting", (mCG->getU32("TestSetting") == 12)); + } + + //save settings to files + template<> template<> + void control_group_t::test<2>() + { + int results = mCG->loadFromFile(mTestConfigFile.c_str()); + mCG->setU32("TestSetting", 13); + ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13); + LLControlGroup test_cg; + LLString temp_test_file = (mTestConfigDir + "setting_llsd_temp.xml"); + mCG->saveToFile(temp_test_file.c_str(), TRUE); + results = test_cg.loadFromFile(temp_test_file.c_str()); + ensure("number of changed settings loaded", (results == 1)); + ensure("value of changed settings loaded", (test_cg.getU32("TestSetting") == 13)); + } + + //priorities + template<> template<> + void control_group_t::test<3>() + { + int results = mCG->loadFromFile(mTestConfigFile.c_str()); + LLControlVariable* control = mCG->getControl("TestSetting"); + LLSD new_value = 13; + control->setValue(new_value, FALSE); + ensure_equals("value of changed setting", mCG->getU32("TestSetting"), 13); + LLControlGroup test_cg; + LLString temp_test_file = (mTestConfigDir + "setting_llsd_persist_temp.xml"); + mCG->saveToFile(temp_test_file.c_str(), TRUE); + results = test_cg.loadFromFile(temp_test_file.c_str()); + //If we haven't changed any settings, then we shouldn't have any settings to load + ensure("number of non-persisted changed settings loaded", (results == 0)); + } + + //listeners + template<> template<> + void control_group_t::test<4>() + { + int results = mCG->loadFromFile(mTestConfigFile.c_str()); + ensure("number of settings", (results == 1)); + mCG->getControl("TestSetting")->getSignal()->connect(boost::bind(&this->handleListenerTest, _1)); + mCG->setU32("TestSetting", 13); + ensure("listener fired on changed setting", mListenerFired); + } + +} diff --git a/linden/indra/test/llmessageconfig_tut.cpp b/linden/indra/test/llmessageconfig_tut.cpp index b709bfc..e5dd328 100644 --- a/linden/indra/test/llmessageconfig_tut.cpp +++ b/linden/indra/test/llmessageconfig_tut.cpp @@ -35,7 +35,6 @@ #include "lltut.h" #include "llsdserialize.h" #include "llfile.h" -#include "lldir.h" #include "lltimer.h" #include "llframetimer.h" #include "llsdutil.h" @@ -51,7 +50,11 @@ namespace tut random.generate(); // generate temp dir std::ostringstream oStr; +#if LL_WINDOWS + oStr << "llmessage-config-test-" << random; +#else oStr << "/tmp/llmessage-config-test-" << random; +#endif mTestConfigDir = oStr.str(); LLFile::mkdir(mTestConfigDir.c_str()); writeConfigFile(LLSD()); @@ -199,4 +202,23 @@ namespace tut LLMessageConfig::getServerDefaultFlavor(), LLMessageConfig::LLSD_FLAVOR); } + + template<> template<> + void LLMessageConfigTestObject::test<8>() + // tests that config changes are picked up/refreshed periodically + { + LLSD config; + config["serverDefaults"]["simulator"] = "template"; + config["messages"]["msg1"]["flavor"] = "llsd"; + config["messages"]["msg1"]["only-send-latest"] = true; + config["messages"]["msg2"]["flavor"] = "llsd"; + config["messages"]["msg2"]["only-send-latest"] = false; + LLMessageConfig::useConfig(config); + ensure_equals("Ensure msg1 exists, sent latest-only", + LLMessageConfig::onlySendLatest("msg1"), + true); + ensure_equals("Ensure msg2 exists, sent latest-only", + LLMessageConfig::onlySendLatest("msg2"), + false); + } } diff --git a/linden/indra/test/llmessagetemplateparser_tut.cpp b/linden/indra/test/llmessagetemplateparser_tut.cpp index fe8e34c..7634e90 100644 --- a/linden/indra/test/llmessagetemplateparser_tut.cpp +++ b/linden/indra/test/llmessagetemplateparser_tut.cpp @@ -56,7 +56,7 @@ namespace tut char * prehash(const char * name) { - return gMessageStringTable.getString(name); + return LLMessageStringTable::getInstance()->getString(name); } void ensure_block_attributes(std::string identifier, diff --git a/linden/indra/test/llnamevalue_tut.cpp b/linden/indra/test/llnamevalue_tut.cpp index d38913a..f604e84 100644 --- a/linden/indra/test/llnamevalue_tut.cpp +++ b/linden/indra/test/llnamevalue_tut.cpp @@ -42,133 +42,7 @@ namespace tut { namevalue_test() { - mExpectedNameValueReference.string = NULL; - mExpectedNameValueType = NVT_NULL; - mCallbackCount = 0; } - - ~namevalue_test() - { - reset(); - }; - - void reset() - { - switch(mExpectedNameValueType) - { - case NVT_STRING: - case NVT_ASSET: - delete [] mExpectedNameValueReference.string; - mExpectedNameValueReference.string = NULL; - break; - case NVT_F32: - delete mExpectedNameValueReference.f32; - mExpectedNameValueReference.f32 = NULL; - break; - case NVT_S32: - delete mExpectedNameValueReference.s32; - mExpectedNameValueReference.s32 = NULL; - break; - case NVT_U32: - delete mExpectedNameValueReference.u32; - mExpectedNameValueReference.u32 = NULL; - break; - case NVT_VEC3: - delete mExpectedNameValueReference.vec3; - mExpectedNameValueReference.vec3 = NULL; - break; - case NVT_U64: - delete mExpectedNameValueReference.u64; - mExpectedNameValueReference.u64 = NULL; - default: - break; - } - - mExpectedNameValueType = NVT_NULL; - mCallbackCount = 0; - } - - void setExpectedResult(ENameValueType type, void* value) - { - reset(); - mExpectedNameValueType = type; - switch(type) - { - case NVT_STRING: - case NVT_ASSET: - mExpectedNameValueReference.string = new char[strlen((const char*) value)+1]; - strcpy(mExpectedNameValueReference.string, (const char*) value); - break; - case NVT_F32: - mExpectedNameValueReference.f32 = new F32(*((F32*) value)); - break; - case NVT_S32: - mExpectedNameValueReference.s32 = new S32(*((S32*) value)); - break; - case NVT_U32: - mExpectedNameValueReference.u32 = new U32(*((U32*) value)); - break; - case NVT_VEC3: - mExpectedNameValueReference.vec3 = new LLVector3(*((LLVector3*) value)); - break; - case NVT_U64: - mExpectedNameValueReference.u64 = new U64(*((U64*) value)); - default: - break; - } - } - - void verifyChange(LLNameValue* changed) - { - std::string str = ""; - str += "Expected Value of type: "; - str += NameValueTypeStrings[mExpectedNameValueType]; - str += "not equal"; - - switch(mExpectedNameValueType) - { - case NVT_STRING: - ensure_memory_matches(str.c_str(), changed->getString(), strlen(changed->getString()), mExpectedNameValueReference.string, strlen(mExpectedNameValueReference.string)); - break; - case NVT_ASSET: - ensure_memory_matches(str.c_str(), changed->getAsset(), strlen(changed->getAsset()), mExpectedNameValueReference.string, strlen(mExpectedNameValueReference.string)); - break; - case NVT_F32: - ensure(str, *(changed->getF32()) == *mExpectedNameValueReference.f32); - break; - case NVT_S32: - ensure(str, *(changed->getS32()) == *mExpectedNameValueReference.s32); - break; - case NVT_U32: - ensure(str, *(changed->getU32()) == *mExpectedNameValueReference.u32); - break; - case NVT_VEC3: - ensure(str, *(changed->getVec3()) == *mExpectedNameValueReference.vec3); - break; - case NVT_U64: - ensure(str, *(changed->getU64()) == *mExpectedNameValueReference.u64); - break; - default: - break; - } - } - - void HandleCallback(LLNameValue* changed) - { - mCallbackCount++; - verifyChange(changed); - ensure("Callback called more than once", mCallbackCount == 1); - } - - static void NameValueCallbackFunction(LLNameValue* changed, void** data) - { - namevalue_test* pNameValue = (namevalue_test*)data; - pNameValue->HandleCallback(changed); - } - - ENameValueType mExpectedNameValueType; - UNameValueReference mExpectedNameValueReference; - int mCallbackCount; }; typedef test_group namevalue_t; typedef namevalue_t::object namevalue_object_t; @@ -225,33 +99,33 @@ namespace tut ensure("3. getS32 failed", *nValue3.getS32() == -43456787); ensure("sendToData or sendToViewer failed", nValue3.sendToData() && !nValue3.sendToViewer()); - LLNameValue nValue4("SecondLife", "<1.0, 2.0, 3.0>", "VEC3", "CB", "SV"); + LLNameValue nValue4("SecondLife", "<1.0, 2.0, 3.0>", "VEC3", "RW", "SV"); LLVector3 llvec4(1.0, 2.0, 3.0); ensure("4. getTypeEnum failed", nValue4.getTypeEnum() == NVT_VEC3); - ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_CALLBACK); + ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_READ_WRITE); ensure("4. getSendtoEnum failed", nValue4.getSendtoEnum() == NVS_SIM_VIEWER); ensure("4. getVec3 failed", *nValue4.getVec3() == llvec4); ensure("4. sendToData or sendToViewer failed", !nValue4.sendToData() && nValue4.sendToViewer()); - LLNameValue nValue5("SecondLife", "-1.0, 2.4, 3", "VEC3", "CALLBACK", "SIM_VIEWER"); + LLNameValue nValue5("SecondLife", "-1.0, 2.4, 3", "VEC3", "RW", "SIM_VIEWER"); LLVector3 llvec5(-1.0f, 2.4f, 3); ensure("5. getTypeEnum failed", nValue5.getTypeEnum() == NVT_VEC3); - ensure("5. getClassEnum failed", nValue5.getClassEnum() == NVC_CALLBACK); + ensure("5. getClassEnum failed", nValue5.getClassEnum() == NVC_READ_WRITE); ensure("5. getSendtoEnum failed", nValue5.getSendtoEnum() == NVS_SIM_VIEWER); ensure("5. getVec3 failed", *nValue5.getVec3() == llvec5); ensure("5. sendToData or sendToViewer failed", !nValue5.sendToData() && nValue5.sendToViewer()); - LLNameValue nValue6("SecondLife", "89764323", "U32", "CALLBACK", "DSV"); + LLNameValue nValue6("SecondLife", "89764323", "U32", "RW", "DSV"); ensure("6. getTypeEnum failed", nValue6.getTypeEnum() == NVT_U32); - ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_CALLBACK); + ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_READ_WRITE); ensure("6. getSendtoEnum failed", nValue6.getSendtoEnum() == NVS_DATA_SIM_VIEWER); ensure("6. getU32 failed", *nValue6.getU32() == 89764323); ensure("6. sendToData or sendToViewer failed", nValue6.sendToData() && nValue6.sendToViewer()); - LLNameValue nValue7("SecondLife", "89764323323232", "U64", "CALLBACK", "SIM_SPACE_VIEWER"); + LLNameValue nValue7("SecondLife", "89764323323232", "U64", "RW", "SIM_SPACE_VIEWER"); U64 u64_7 = U64L(89764323323232); ensure("7. getTypeEnum failed", nValue7.getTypeEnum() == NVT_U64); - ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_CALLBACK); + ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_READ_WRITE); ensure("7. getSendtoEnum failed", nValue7.getSendtoEnum() == NVS_DATA_SIM_VIEWER); ensure("7. getU32 failed", *nValue7.getU64() == u64_7); ensure("7. sendToData or sendToViewer failed", nValue7.sendToData() && nValue7.sendToViewer()); @@ -288,30 +162,30 @@ namespace tut ensure("3. getSendtoEnum failed", nValue3.getSendtoEnum() == NVS_SIM); ensure("3. getS32 failed", *nValue3.getS32() == -43456787); - LLNameValue nValue4("SecondLife", "<1.0, 2.0, 3.0>", "VEC3", "CB"); + LLNameValue nValue4("SecondLife", "<1.0, 2.0, 3.0>", "VEC3", "RW"); LLVector3 llvec4(1.0, 2.0, 3.0); ensure("4. getTypeEnum failed", nValue4.getTypeEnum() == NVT_VEC3); - ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_CALLBACK); + ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_READ_WRITE); ensure("4. getSendtoEnum failed", nValue4.getSendtoEnum() == NVS_SIM); ensure("4. getVec3 failed", *nValue4.getVec3() == llvec4); - LLNameValue nValue5("SecondLife", "-1.0, 2.4, 3", "VEC3", "CALLBACK"); + LLNameValue nValue5("SecondLife", "-1.0, 2.4, 3", "VEC3", "RW"); LLVector3 llvec5(-1.0f, 2.4f, 3); ensure("5. getTypeEnum failed", nValue5.getTypeEnum() == NVT_VEC3); - ensure("5. getClassEnum failed", nValue5.getClassEnum() == NVC_CALLBACK); + ensure("5. getClassEnum failed", nValue5.getClassEnum() == NVC_READ_WRITE); ensure("5. getSendtoEnum failed", nValue5.getSendtoEnum() == NVS_SIM); ensure("5. getVec3 failed", *nValue5.getVec3() == llvec5); - LLNameValue nValue6("SecondLife", "89764323", "U32", "CALLBACK"); + LLNameValue nValue6("SecondLife", "89764323", "U32", "RW"); ensure("6. getTypeEnum failed", nValue6.getTypeEnum() == NVT_U32); - ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_CALLBACK); + ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_READ_WRITE); ensure("6. getSendtoEnum failed", nValue6.getSendtoEnum() == NVS_SIM); ensure("6. getU32 failed", *nValue6.getU32() == 89764323); - LLNameValue nValue7("SecondLife", "89764323323232", "U64", "CALLBACK"); + LLNameValue nValue7("SecondLife", "89764323323232", "U64", "RW"); U64 u64_7 = U64L(89764323323232); ensure("7. getTypeEnum failed", nValue7.getTypeEnum() == NVT_U64); - ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_CALLBACK); + ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_READ_WRITE); ensure("7. getSendtoEnum failed", nValue7.getSendtoEnum() == NVS_SIM); ensure("7. getU32 failed", *nValue7.getU64() == u64_7); } @@ -343,208 +217,118 @@ namespace tut ensure("3. getClassEnum failed", nValue3.getClassEnum() == NVC_READ_ONLY); ensure("3. getSendtoEnum failed", nValue3.getSendtoEnum() == NVS_SIM); - skip_fail("NVC_CALLBACK does not parse."); - - LLNameValue nValue4("SecondLife", "VEC3", "CALLBACK"); + LLNameValue nValue4("SecondLife", "VEC3", "READ_WRITE"); ensure("4. getTypeEnum failed", nValue4.getTypeEnum() == NVT_VEC3); - ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_CALLBACK); + ensure("4. getClassEnum failed", nValue4.getClassEnum() == NVC_READ_WRITE); ensure("4. getSendtoEnum failed", nValue4.getSendtoEnum() == NVS_SIM); - LLNameValue nValue6("SecondLife", "U32", "CALLBACK"); + LLNameValue nValue6("SecondLife", "U32", "READ_WRITE"); ensure("6. getTypeEnum failed", nValue6.getTypeEnum() == NVT_U32); - ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_CALLBACK); + ensure("6. getClassEnum failed", nValue6.getClassEnum() == NVC_READ_WRITE); ensure("6. getSendtoEnum failed", nValue6.getSendtoEnum() == NVS_SIM); - LLNameValue nValue7("SecondLife", "U64", "CALLBACK"); + LLNameValue nValue7("SecondLife", "U64", "READ_WRITE"); ensure("7. getTypeEnum failed", nValue7.getTypeEnum() == NVT_U64); - ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_CALLBACK); + ensure("7. getClassEnum failed", nValue7.getClassEnum() == NVC_READ_WRITE); ensure("7. getSendtoEnum failed", nValue7.getSendtoEnum() == NVS_SIM); } template<> template<> void namevalue_object_t::test<5>() { - skip_fail("callback will be called more than once."); - LLNameValue nValue("SecondLife", "This is a test", "STRING", "CB", "SIM", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "This is a test", "STRING", "RW", "SIM"); ensure("getString failed", (0 == strcmp(nValue.getString(),"This is a test"))); - reset(); - - setExpectedResult(NVT_STRING, (void*)"New Value"); - nValue.setString("New Value"); - ensure("String nonzero failed", nValue.nonzero() == TRUE); - reset(); - setExpectedResult(NVT_STRING, (void*)""); - nValue.setString(""); - ensure("String nonzero failed", nValue.nonzero() == FALSE); - reset(); } template<> template<> void namevalue_object_t::test<6>() { - skip_fail("callback will be called more than once."); - LLNameValue nValue("SecondLife", "This is a test", "ASSET", "CALLBACK", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "This is a test", "ASSET", "RW", "S"); ensure("getAsset failed", (0 == strcmp(nValue.getAsset(),"This is a test"))); - reset(); - - setExpectedResult(NVT_ASSET, (void*)"New Value"); - nValue.setAsset("New Value"); - reset(); } template<> template<> void namevalue_object_t::test<7>() { - skip_fail("callback will be called more than once."); - LLNameValue nValue("SecondLife", "555555", "F32", "CB", "SIM", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "555555", "F32", "RW", "SIM"); ensure("getF32 failed",*nValue.getF32() == 555555.f); - reset(); - - F32 fVal = 0.1f; - setExpectedResult(NVT_F32, &fVal); - nValue.setF32(fVal); - - fVal = -11111.1f; - setExpectedResult(NVT_F32, &fVal); - nValue.setF32(fVal); - ensure("F32 nonzero failed", nValue.nonzero() == TRUE); - reset(); - - fVal = 0.; - setExpectedResult(NVT_F32, &fVal); - nValue.setF32(fVal); - ensure("F32 nonzero failed", nValue.nonzero() == FALSE); - reset(); } template<> template<> void namevalue_object_t::test<8>() { - skip_fail("callback will be called more than once."); - LLNameValue nValue("SecondLife", "-5555", "S32", "CB", "SIM", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "-5555", "S32", "RW", "SIM"); ensure("getS32 failed", *nValue.getS32() == -5555); - reset(); S32 sVal = 0x7FFFFFFF; - setExpectedResult(NVT_S32, &sVal); nValue.setS32(sVal); + ensure("getS32 failed", *nValue.getS32() == sVal); sVal = -0x7FFFFFFF; - setExpectedResult(NVT_S32, &sVal); nValue.setS32(sVal); - ensure("S32 nonzero failed", nValue.nonzero() == TRUE); - reset(); + ensure("getS32 failed", *nValue.getS32() == sVal); sVal = 0; - setExpectedResult(NVT_S32, &sVal); nValue.setS32(sVal); - ensure("S32 nonzero failed", nValue.nonzero() == FALSE); - reset(); + ensure("getS32 failed", *nValue.getS32() == sVal); } template<> template<> void namevalue_object_t::test<9>() { - LLNameValue nValue("SecondLife", "<-3, 2, 1>", "VEC3", "CB", "SIM", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "<-3, 2, 1>", "VEC3", "RW", "SIM"); LLVector3 vecExpected(-3, 2, 1); LLVector3 vec; nValue.getVec3(vec); ensure("getVec3 failed", vec == vecExpected); - reset(); - - vecExpected.setVec(2, -1, 0); - setExpectedResult(NVT_VEC3, &vecExpected); - nValue.setVec3(vecExpected); - ensure("VEC3 nonzero failed", nValue.nonzero() == TRUE); - reset(); - - vecExpected.setVec(0, 0, 0); - setExpectedResult(NVT_VEC3, &vecExpected); - nValue.setVec3(vecExpected); - ensure("VEC3 nonzero failed", nValue.nonzero() == FALSE); - reset(); } template<> template<> void namevalue_object_t::test<10>() { - LLNameValue nValue("SecondLife", "12345678", "U32", "CB", "SIM", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "12345678", "U32", "RW", "SIM"); ensure("getU32 failed",*nValue.getU32() == 12345678); U32 val = 0xFFFFFFFF; - setExpectedResult(NVT_U32, &val); nValue.setU32(val); - ensure("U32 nonzero failed", nValue.nonzero() == TRUE); - reset(); + ensure("U32 max", *nValue.getU32() == val); val = 0; - setExpectedResult(NVT_U32, &val); nValue.setU32(val); - ensure("U32 nonzero failed", nValue.nonzero() == FALSE); - reset(); + ensure("U32 min", *nValue.getU32() == val); } template<> template<> void namevalue_object_t::test<11>() { - skip_fail("incomplete support for U64."); - LLNameValue nValue("SecondLife", "44444444444", "U64", "CB", "SIM", NameValueCallbackFunction, (void**) this); + //skip_fail("incomplete support for U64."); + LLNameValue nValue("SecondLife", "44444444444", "U64", "RW", "SIM"); ensure("getU64 failed",*nValue.getU64() == U64L(44444444444)); - ensure("U64 nonzero failed", nValue.nonzero() == TRUE); // there is no LLNameValue::setU64() } - template<> template<> - void namevalue_object_t::test<12>() - { - LLNameValue nValue("SecondLife F32 RW SIM -333.337600"); - F32 val = nValue.magnitude(); - ensure_equals("F32 magnitude failed", val, 333.337600f); - - LLNameValue nValue1("SecondLife STRING RW SIM 3300"); - val = nValue1.magnitude(); - ensure_equals("STRING magnitude failed",val,4.0f); - - LLNameValue nValue2("SecondLife S32 RW SIM -3300"); - val = nValue2.magnitude(); - ensure_equals("S32 magnitude failed", val, 3300.); - - LLNameValue nValue3("SecondLife U32 RW SIM 3300"); - val = nValue3.magnitude(); - ensure_equals("U32 magnitude failed", val, 3300.); - - LLNameValue nValue4("SecondLife VEC3 RW SIM <1,2,3>"); - LLVector3 vec(1,2,3); - val = nValue4.magnitude(); - ensure_equals("VEC3 magnitude failed", val, vec.magVec()); - - skip_fail("incomplete support for U64."); - LLNameValue nValue5("SecondLife U64 RW SIM 12345"); - val = nValue5.magnitude(); - ensure_equals("U62 magnitude failed", val, 12345); - } template<> template<> - void namevalue_object_t::test<13>() + void namevalue_object_t::test<12>() { - skip_fail("incomplete support for U64."); + //skip_fail("incomplete support for U64."); LLNameValue nValue("SecondLife U64 RW DSV 44444444444"); std::string ret_str = nValue.printNameValue(); ensure_equals("1:printNameValue failed",ret_str,"SecondLife U64 RW DSV 44444444444"); LLNameValue nValue1(ret_str.c_str()); - ensure_equals("Serialization of printNameValue failed", nValue, nValue1); + ensure_equals("Serialization of printNameValue failed", *nValue.getU64(), *nValue1.getU64()); } template<> template<> - void namevalue_object_t::test<14>() + void namevalue_object_t::test<13>() { LLNameValue nValue("SecondLife STRING RW DSV 44444444444"); std::string ret_str = nValue.printData(); @@ -556,7 +340,7 @@ namespace tut } template<> template<> - void namevalue_object_t::test<15>() + void namevalue_object_t::test<14>() { LLNameValue nValue("SecodLife STRING RW SIM 22222"); std::ostringstream stream1,stream2,stream3, stream4, stream5; @@ -575,271 +359,39 @@ namespace tut stream4<< nValue3; ensure_equals("U32 << failed",stream4.str(),"122222"); - skip_fail("incomplete support for U64."); - LLNameValue nValue4("SecodLife U64 RW SIM 22222"); - stream5<< nValue4; - ensure("U64 << failed",0 == strcmp((stream5.str()).c_str(),"22222")); - } - - template<> template<> - void namevalue_object_t::test<16>() - { - LLNameValue nValue1("SecondLife STRING RW DSV 44444"); - LLNameValue nValue2("SecondLife STRING RW SIM 33333"); - LLNameValue nValue3("SecondLife"); - nValue3 = nValue1 + nValue2; - ensure("1:operator+ failed",(0==strcmp(nValue3.getString(),"4444433333"))); - - LLNameValue nValue4("SecondLife F32 R DSV 44444"); - LLNameValue nValue5("SecondLife F32 RW SIM 33333"); - LLNameValue nValue6("SecondLife"); - nValue6 = nValue4 + nValue5; - ensure_equals("2:operator+ failed",*nValue6.getF32(),77777.0); - - LLNameValue nValue7("SecondLife F32 R DSV 44444"); - LLNameValue nValue8("SecondLife S32 RW SIM 33333"); - LLNameValue nValue9("SecondLife F32"); - nValue9 = nValue7 + nValue8; - ensure_equals("3:operator+ failed",*nValue9.getF32(),77777.0); - - LLNameValue nValue10("SecondLife VEC3 RW SIM <4, 4, 4>"); - LLNameValue nValue11("SecondLife VEC3 RW SV <3, 3, 3>"); - LLNameValue nValue12("SecondLife VEC3"); - nValue12 = nValue10 + nValue11; - LLVector3 vec(7,7,7); - ensure_equals("4:operator+ failed",*nValue12.getVec3(), vec); - } - - template<> template<> - void namevalue_object_t::test<17>() - { - LLNameValue nValue7(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue8(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue9(" SecondLife F32"); - nValue9 = nValue7 - nValue8; - ensure_equals("1:operator- failed",*nValue9.getF32(),-11111.f); - - LLNameValue nValue10(" SecondLife VEC3 RW SIM <2, 2, 2>"); - LLNameValue nValue11(" SecondLife VEC3 RW SIM <3, 3, 3>"); - LLNameValue nValue12(" SecondLife VEC3"); - LLVector3 vec(-1,-1,-1); - nValue12 = nValue10 - nValue11; - ensure_equals("2:operator- failed",*nValue12.getVec3(), vec); - } - - template<> template<> - void namevalue_object_t::test<18>() - { - - LLNameValue nValue1(" SecondLife F32 RW SIM 22222"); - LLNameValue nValue2(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue3(" SecondLife F32"); - nValue3 = nValue1 * nValue2; - ensure_equals("1:operator* failed",*nValue3.getF32(),740725926.f); - - LLNameValue nValue4(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue5(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue6(" SecondLife F32"); - nValue6 = nValue4 * nValue5; - ensure_equals("2:operator* failed",*nValue6.getF32(),740725926.f); - - LLNameValue nValue10(" SecondLife VEC3 RW SIM <2, 2, 2>"); - LLNameValue nValue11(" SecondLife VEC3 RW SIM <3, 3, 3>"); - LLNameValue nValue12(" SecondLife F32"); - LLVector3 vec1(2,2,2); - LLVector3 vec2(3,3,3); - nValue12 = nValue10 * nValue11; - ensure_equals("2:operator* failed",*nValue12.getF32(), (vec1 * vec2)); + // I don't think we use U64 name value pairs. JC + //skip_fail("incomplete support for U64."); + //LLNameValue nValue4("SecodLife U64 RW SIM 22222"); + //stream5<< nValue4; + //ensure("U64 << failed",0 == strcmp((stream5.str()).c_str(),"22222")); } template<> template<> - void namevalue_object_t::test<19>() - { - LLNameValue nValue1(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue2(" Virtual F32 RW SIM 44444"); - LLNameValue nValue3(" SecondLife F32"); - nValue3 = nValue1 / nValue2; - ensure_equals("1:operator/ failed",*nValue3.getF32(),0.5); - - LLNameValue nValue4(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue5(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue6(" SecondLife F32"); - nValue6 = nValue4 / nValue5; - ensure_equals("2:operator/ failed",*nValue6.getF32(),1.5); - } - - template<> template<> - void namevalue_object_t::test<20>() - { - LLNameValue nValue1(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue2(" Virtual S32 RW SIM 33333"); - LLNameValue nValue3(" SecondLife S32"); - nValue3 = nValue1 % nValue2; - ensure_equals("1:operator% failed",*nValue3.getS32(),22222); - - LLNameValue nValue4(" SecondLife U32 RW SIM 3"); - LLNameValue nValue5(" SecondLife S32 RW SIM 2"); - LLNameValue nValue6(" SecondLife S32"); - nValue6 = nValue4 % nValue5; - ensure_equals("2:operator% failed",*nValue6.getS32(),1); - - LLNameValue nValue10(" SecondLife VEC3 RW SIM <4, 5, 6>"); - LLNameValue nValue11(" SecondLife VEC3 RW SIM <1, 2, 3>"); - LLNameValue nValue12(" SecondLife VEC3"); - LLVector3 vec1(4,5,6); - LLVector3 vec2(1,2,3); - LLVector3 vec3(vec1 % vec2); - nValue12 = nValue10 % nValue11; - ensure_equals("5:operator% failed",*nValue12.getVec3(), vec3); - } - - template<> template<> - void namevalue_object_t::test<21>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 22222"); - LLNameValue nValue2(" Virtual STRING RW SIM 22222"); - ensure("1:operator== failed", nValue1 == nValue2); - - LLNameValue nValue3(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - ensure("2:operator== failed",!(nValue3 == nValue4)); - - LLNameValue nValue5(" SecondLife STRING RW SIM 22222"); - LLNameValue nValue6(" Virtual STRING RW SIM 33333"); - ensure("3:operator== failed",!(nValue5 == nValue6)); - - LLNameValue nValue7(" SecondLife VEC3 RW SIM <2, 2, 2>"); - LLNameValue nValue8(" Virtual VEC3 RW SIM <2, 2, 2>"); - ensure("4:operator== failed",(nValue7 == nValue8)); - } - - template<> template<> - void namevalue_object_t::test<22>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 22222"); - LLNameValue nValue2(" Virtual STRING RW SIM 33333"); - bool b_ret = (nValue1 <= nValue2) ? 1 : 0; - ensure("1:operator<= failed",(1==b_ret)); - - LLNameValue nValue3(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - b_ret = (nValue3 <= nValue4) ? 1 : 0; - ensure("2:operator<= failed",(0==b_ret)); - } - - template<> template<> - void namevalue_object_t::test<23>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 22222"); - LLNameValue nValue2(" Virtual STRING RW SIM 33333"); - bool b_ret = (nValue1 >= nValue2) ? 1 : 0; - ensure("operator>= failed",!b_ret); - - LLNameValue nValue3(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - b_ret = (nValue3 >= nValue4) ? 1 : 0; - ensure("2:operator<= failed",b_ret); - - } - - template<> template<> - void namevalue_object_t::test<24>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 33333"); - LLNameValue nValue2(" Virtual STRING RW SIM 33333"); - bool b_ret = (nValue1 < nValue2) ? 1 : 0; - ensure("operator< failed",!b_ret); - - LLNameValue nValue3(" SecondLife F32 RW SIM 11111"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - b_ret = (nValue3 < nValue4) ? 1 : 0; - ensure("2:operator< failed",b_ret); - - } - - template<> template<> - void namevalue_object_t::test<25>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 33333"); - LLNameValue nValue2(" Virtual STRING RW SIM 33333"); - bool b_ret = (nValue1 > nValue2) ? 1 : 0; - ensure("1:operator> failed",!b_ret); - - LLNameValue nValue3(" SecondLife F32 RW SIM 11111"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - b_ret = (nValue3 > nValue4) ? 1 : 0; - ensure("2:operator> failed",!b_ret); - - LLNameValue nValue5(" SecondLife S32 RW SIM 22222"); - LLNameValue nValue6(" Virtual F32 RW SIM 11111"); - b_ret = (nValue5 > nValue6) ? 1 : 0; - ensure("3:operator> failed",b_ret); - } - - template<> template<> - void namevalue_object_t::test<26>() - { - LLNameValue nValue1(" SecondLife STRING RW SIM 33333"); - LLNameValue nValue2(" Virtual STRING RW SIM 33333"); - bool b_ret = (nValue1 != nValue2) ? 1 : 0; - ensure("1:operator!= failed",!b_ret); - - LLNameValue nValue3(" SecondLife F32 RW SIM 11111"); - LLNameValue nValue4(" Virtual F32 RW SIM 22222"); - b_ret = (nValue3 != nValue4) ? 1 : 0; - ensure("2:operator!= failed",b_ret); - - } - - - template<> template<> - void namevalue_object_t::test<27>() - { - LLNameValue nValue1(" SecondLife F32 RW SIM 33333"); - LLNameValue nValue2("Virtual"); - nValue2 = -nValue1; - ensure_equals("1:operator unary- failed",*nValue2.getF32(), -33333.f); - - LLNameValue nValue3(" SecondLife U32 RW SIM 11111"); - LLNameValue nValue4("Virtual S32"); - nValue4 = -nValue3; - ensure_equals("2:operator unary- failed",*nValue4.getS32(), -11111); - - LLNameValue nValue5(" SecondLife VEC3 RW SIM <1, 1, 1>"); - LLNameValue nValue6("Virtual VEC3"); - LLVector3 vec(-1, -1, -1); - nValue6 = -nValue5; - ensure_equals("3:operator unary- failed",*nValue6.getVec3(), vec); - } - - template<> template<> - void namevalue_object_t::test<28>() + void namevalue_object_t::test<15>() { - LLNameValue nValue("SecondLife", "This is a test", "ASSET", "R", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue("SecondLife", "This is a test", "ASSET", "R", "S"); ensure("getAsset failed", (0 == strcmp(nValue.getAsset(),"This is a test"))); // this should not have updated as it is read only. nValue.setAsset("New Value should not be updated"); ensure("setAsset on ReadOnly failed", (0 == strcmp(nValue.getAsset(),"This is a test"))); - LLNameValue nValue1("SecondLife", "1234", "U32", "R", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue1("SecondLife", "1234", "U32", "R", "S"); // this should not have updated as it is read only. nValue1.setU32(4567); ensure("setU32 on ReadOnly failed", *nValue1.getU32() == 1234); - LLNameValue nValue2("SecondLife", "1234", "S32", "R", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue2("SecondLife", "1234", "S32", "R", "S"); // this should not have updated as it is read only. nValue2.setS32(4567); ensure("setS32 on ReadOnly failed", *nValue2.getS32() == 1234); - LLNameValue nValue3("SecondLife", "1234", "F32", "R", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue3("SecondLife", "1234", "F32", "R", "S"); // this should not have updated as it is read only. nValue3.setF32(4567); ensure("setF32 on ReadOnly failed", *nValue3.getF32() == 1234); - nValue3 = nValue3 * 2; - ensure("setF32 on ReadOnly failed", *nValue3.getF32() == 1234); - LLNameValue nValue4("SecondLife", "<1,2,3>", "VEC3", "R", "S", NameValueCallbackFunction, (void**) this); + LLNameValue nValue4("SecondLife", "<1,2,3>", "VEC3", "R", "S"); // this should not have updated as it is read only. LLVector3 vec(4,5,6); nValue3.setVec3(vec); diff --git a/linden/indra/test/llsdmessagereader_tut.cpp b/linden/indra/test/llsdmessagereader_tut.cpp index e52809f..04861c4 100755 --- a/linden/indra/test/llsdmessagereader_tut.cpp +++ b/linden/indra/test/llsdmessagereader_tut.cpp @@ -46,7 +46,7 @@ namespace tut const std::string& expected_name) { LLSDMessageReader msg; - msg.setMessage(gMessageStringTable.getString(msg_name.c_str()), msg_data); + msg.setMessage(LLMessageStringTable::getInstance()->getString(msg_name.c_str()), msg_data); ensure_equals("Ensure name", std::string(msg.getMessageName()), expected_name); } diff --git a/linden/indra/test/llstreamtools_tut.cpp b/linden/indra/test/llstreamtools_tut.cpp index 3e89282..c360cc1 100644 --- a/linden/indra/test/llstreamtools_tut.cpp +++ b/linden/indra/test/llstreamtools_tut.cpp @@ -491,7 +491,7 @@ namespace tut is.str(str = "First Second \t \r\n Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n"); actual_result = ""; ret = get_line(actual_result, is); - expected_result = "First Second \t \n"; + expected_result = "First Second \t \r\n"; ensure_equals("get_line: 1", actual_result, expected_result); actual_result = ""; @@ -545,7 +545,6 @@ namespace tut template<> template<> void streamtools_object::test<12>() { - skip_fail("get_line() incorrectly handles lone carriage return."); std::string str; std::string expected_result; std::string actual_result; @@ -554,10 +553,10 @@ namespace tut // need to be check if this test case is wrong or the implementation is wrong. is.clear(); - is.str(str = "Should not skip \r unless they are followed with newline .\r\n"); + is.str(str = "Should not skip lone \r.\r\n"); actual_result = ""; ret = get_line(actual_result, is); - expected_result = "Should not skip \r unless they are followed with newline .\n"; + expected_result = "Should not skip lone \r.\r\n"; ensure_equals("get_line: carriage return skipped even though not followed by newline", actual_result, expected_result); } @@ -804,37 +803,6 @@ namespace tut template<> template<> void streamtools_object::test<21>() { - skip_fail("get_brace_count() has bugs."); - - std::string str; - std::string expected_result; - int count; - - str = " { "; - count = get_brace_count(str); - ensure("get_brace_count: 1 for {", count == 1); - - str = "\t}\t\t \n"; - count = get_brace_count(str); - ensure("get_brace_count: 1 for {", count == -1); - - str = "\t\t\t \n"; - count = get_brace_count(str); - ensure("get_brace_count: 0 for no braces", count == 0); - - str = "{ Remaining line not empty\n"; - count = get_brace_count(str); - ensure("get_brace_count: 0 for remainign line not empty", count == 0); - - /* shouldn't this return 1? */ - str = "{ /*Remaining line in comment*/\n"; - count = get_brace_count(str); - ensure("get_brace_count: 1 for { with remaining line in comment", count == 1); - - /* shouldn't this return -1? */ - str = " } //Remaining line in comment \n"; - count = get_brace_count(str); - ensure("get_brace_count: -1 for } with remaining line in comment", count == -1); } //testcases for get_keyword_and_value() @@ -860,8 +828,6 @@ namespace tut template<> template<> void streamtools_object::test<23>() { - skip_fail("get_keyword_and_value() has bugs."); - std::string s; std::string keyword = "SOME PRIOR KEYWORD"; std::string value = "SOME PRIOR VALUE"; @@ -875,8 +841,6 @@ namespace tut template<> template<> void streamtools_object::test<24>() { - skip_fail("get_keyword_and_value() has bugs."); - std::string s; std::string keyword = "SOME PRIOR KEYWORD"; std::string value = "SOME PRIOR VALUE"; diff --git a/linden/indra/test/lltemplatemessagebuilder_tut.cpp b/linden/indra/test/lltemplatemessagebuilder_tut.cpp index 1b3d600..86b1fbd 100644 --- a/linden/indra/test/lltemplatemessagebuilder_tut.cpp +++ b/linden/indra/test/lltemplatemessagebuilder_tut.cpp @@ -65,7 +65,7 @@ namespace tut LL_VERSION_PATCH, FALSE, "notasharedsecret"); - init_prehash_data(); + //init_prehash_data(); init = true; } return LLMessageTemplate(_PREHASH_TestMessage, 1, MFT_HIGH); diff --git a/linden/indra/test/lltut.h b/linden/indra/test/lltut.h index e5c2419..4e4af73 100644 --- a/linden/indra/test/lltut.h +++ b/linden/indra/test/lltut.h @@ -44,6 +44,16 @@ class LLSD; namespace tut { + inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) + { + if(!is_approx_equal_fraction(actual, expected, frac_bits)) + { + std::stringstream ss; + ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; + throw tut::failure(ss.str().c_str()); + } + } + inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) { if(!is_approx_equal_fraction(actual, expected, frac_bits)) diff --git a/linden/indra/test/mass_properties_tut.cpp b/linden/indra/test/mass_properties_tut.cpp new file mode 100644 index 0000000..08cfeac --- /dev/null +++ b/linden/indra/test/mass_properties_tut.cpp @@ -0,0 +1,1008 @@ +/** + * @file mass_properties.cpp + * @author andrew@lindenlab.com + * @date 2007-12-20 + * @brief Tests for the LLPrimMassProperties and LLObjectMassProperties classes + * + * $LicenseInfo:firstyear=2007&license=internal$ + * + * Copyright (c) 2007-2008, Linden Research, Inc. + * + * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of + * this source code is governed by the Linden Lab Source Code Disclosure + * Agreement ("Agreement") previously entered between you and Linden + * Lab. By accessing, using, copying, modifying or distributing this + * software, you acknowledge that you have been informed of your + * obligations under the Agreement and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lltut.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +const F32 SMALL_RELATIVE_ERROR = 0.001f; // 0.1% +const F32 SQRT_THREE = 1.732050808f; +const F32 SQRT_SIX = 2.449489743f; + +namespace tut +{ + struct mass_properties_data + { + LLPrimMassProperties prim_properties; + LLObjectMassProperties object_properties; + }; + + typedef test_group mass_properties_group; + typedef mass_properties_group::object mass_properties; + tut::mass_properties_group mp_test_group("mass properties"); + + template<> template<> + void mass_properties::test<1>() + { + // test SPHERE mass properties + LLPrimMassProperties prim_sphere; + prim_sphere.setUnitSphere(); + + F32 density = 1000.f; + F32 radius = 5.f; + F32 diameter = 2.f * radius; + LLVector3 scale(diameter, diameter, diameter); + LLObjectMassProperties obj_sphere(prim_sphere, scale, density); + + F32 computed_mass = obj_sphere.getMass(); + //LLVector3 center_of_mass + //obj_sphere.getCenterOfMass(center_of_mass); + LLMatrix3 inertia; + obj_sphere.getInertiaLocal(inertia); + F32 computed_inertia_eigenvalue = inertia.mMatrix[0][0]; + + + // volume is normalized for scale = <1,1,1> + // V = 4/3 * PI * r^3 + // inertia_eigenvalue = (2/5) * M * r^2 + F32 volume = ( 4.f / 3.f ) * radius * radius * radius * F_PI; + F32 expected_mass = density * volume; + F32 expected_inertia_eigenvalue = ( 2.f / 5.f ) * expected_mass * radius * radius; + + F32 error = fabs(computed_mass - expected_mass) / expected_mass; + ensure("expected sphere mass should match computed", error < SMALL_RELATIVE_ERROR); + + error = fabs(computed_inertia_eigenvalue - expected_inertia_eigenvalue) / expected_inertia_eigenvalue; + ensure("expected sphere inertia should match computed", error < SMALL_RELATIVE_ERROR); + } + + template<> template<> + void mass_properties::test<2>() + { + // test LLInertiaTensorUtils + + // define a known inertia tensor in the center of mass frame + // from the numerical example in this paper: + // http://www.journaldatabase.org/articles/87064/Explicit_Exact_Formulas_f.html + F32 known_mass = 1873.23f; + LLVector3 known_center( 0.f, 0.f, 0.f ); + LLMatrix3 known_inertia; + known_inertia.mMatrix[0][0] = 43520.33257f; + known_inertia.mMatrix[1][1] = 194711.28938f; + known_inertia.mMatrix[2][2] = 191168.76173f; + + known_inertia.mMatrix[0][1] = -11996.20119f; + known_inertia.mMatrix[1][0] = -11996.20119f; + + known_inertia.mMatrix[0][2] = 46343.16662f; + known_inertia.mMatrix[2][0] = 46343.16662f; + + known_inertia.mMatrix[2][1] = -4417.66150f; + known_inertia.mMatrix[1][2] = -4417.66150f; + + // the following two shifts should have null effect + { + LLVector3 first_shift(2.f, 3.f, 4.f); + LLVector3 second_shift = - first_shift; + LLMatrix3 inertia = known_inertia; + + LLInertiaTensorUtils::shiftCenteredInertiaTensor(inertia, first_shift, known_mass); + LLInertiaTensorUtils::centerInertiaTensor(inertia, second_shift, known_mass); + + // we should now have the same inertia with which we started + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + F32 error = fabs(1.f - inertia.mMatrix[i][j] / known_inertia.mMatrix[i][j]); + ensure("LLInertiaTensorUtils shift+sclae-shift-scale should be no-op", error < SMALL_RELATIVE_ERROR); + } + } + } + + // the following series operations should have null effect + { + LLVector3 first_shift(1.f, 5.f, 10.f); + LLVector3 second_scale(2.f, 3.f, 4.f); + LLVector3 third_shift; + LLVector3 fourth_scale; + for (S32 i = 0; i < 3; ++i) + { + third_shift.mV[i] = -first_shift.mV[i] * second_scale.mV[i]; + fourth_scale.mV[i] = 1.f / second_scale.mV[i]; + } + + F32 mass = known_mass; + LLVector3 center = known_center; + LLMatrix3 inertia = known_inertia; + + // first + LLInertiaTensorUtils::shiftCenteredInertiaTensor(inertia, first_shift, mass); + center += first_shift; + + // second + LLInertiaTensorUtils::scaleInertiaTensor(inertia, second_scale); + mass *= second_scale.mV[VX] * second_scale.mV[VY] * second_scale.mV[VZ]; + for (S32 i = 0; i < 3; ++i) + { + center.mV[i] *= second_scale.mV[i]; + } + + // third + LLInertiaTensorUtils::centerInertiaTensor(inertia, third_shift, mass); + center -= third_shift; + + // foruth + LLInertiaTensorUtils::scaleInertiaTensor(inertia, fourth_scale); + + // we should now have the same inertia with which we started + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + F32 error = fabs(1.f - inertia.mMatrix[i][j] / known_inertia.mMatrix[i][j]); + ensure("LLInertiaTensorUtils shift+sclae-shift-scale should be no-op", error < SMALL_RELATIVE_ERROR); + } + } + } + } + + template<> template<> + void mass_properties::test<3>() + { + // test tetrahedral decomposition of unit tetrahedron centered on origin + std::vector< LLVector3 > points; + points.push_back( LLVector3( 0.f, 0.f, 0.f ) ); + points.push_back( LLVector3( 1.f, 0.f, 0.f ) ); + points.push_back( LLVector3( 0.5f, 0.5f * SQRT_THREE, 0.f) ); + points.push_back( LLVector3( 0.5f, SQRT_THREE / 6.f, SQRT_SIX / 3.f) ); + + // compute the center + LLVector3 center; + for (S32 i = 0; i < (S32)points.size(); ++i) + { + center += points[i]; + } + center *= ( 1.f / F32(points.size()) ); + + // shift all points to center of mass frame + for (S32 i = 0; i < (S32)points.size(); ++i) + { + points[i] -= center; + } + + LLPrimMassProperties tetrahedron; + tetrahedron.addSignedTetrahedron(1.0, points[0], points[1], points[2], points[3]); + // we must manually center the inertia tensor here + // since addSignedTetrahedron() does not do it automatically + tetrahedron.centerInertiaTensor(); + + F32 density = 1.0f; + LLVector3 scale(1.f, 1.f, 1.f); + LLMatrix3 analytic_inertia; + tetrahedron.getScaledInertiaTensor(analytic_inertia, scale, density); + + // compute the mesh + std::vector< S32 > triangle_indices; + triangle_indices.push_back(0); + triangle_indices.push_back(2); + triangle_indices.push_back(1); + + triangle_indices.push_back(0); + triangle_indices.push_back(1); + triangle_indices.push_back(3); + + triangle_indices.push_back(0); + triangle_indices.push_back(3); + triangle_indices.push_back(2); + + triangle_indices.push_back(1); + triangle_indices.push_back(2); + triangle_indices.push_back(3); + + // compute the same inertia using a mesh + { + LLPrimMassProperties mesh; + mesh.setUnitMesh(points, triangle_indices); + + // the two properties should agree + F32 error = ( tetrahedron.getVolume() - mesh.getVolume() ) / tetrahedron.getVolume(); + ensure("tetrahedron and mesh volume should match", error < SMALL_RELATIVE_ERROR); + + error = ( tetrahedron.getCenterOfMass() - mesh.getCenterOfMass() ).length(); + ensure("tetrahedron and mesh centers should match", error < SMALL_RELATIVE_ERROR); + + LLMatrix3 mesh_inertia; + mesh.getScaledInertiaTensor(mesh_inertia, scale, density); + + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + // only verify the non-small elements + if (analytic_inertia.mMatrix[i][j] > SMALL_RELATIVE_ERROR) + { + error = fabs(1.f - mesh_inertia.mMatrix[i][j] / analytic_inertia.mMatrix[i][j]); + ensure("LLPrimMassProperties::setUnitMesh() inertia ", error < SMALL_RELATIVE_ERROR); + } + } + } + } + + // shift the whole tetrahedron away from the center of mass and recompute the mesh + { + LLVector3 shift(11.f, 7.f, 3.f); + for (S32 i = 0; i < (S32)points.size(); ++i) + { + points[i] += shift; + } + LLPrimMassProperties mesh; + mesh.setUnitMesh(points, triangle_indices); + + // the two properties should agree + F32 error = ( tetrahedron.getVolume() - mesh.getVolume() ) / tetrahedron.getVolume(); + ensure("tetrahedron and mesh volume should match", error < SMALL_RELATIVE_ERROR); + + LLMatrix3 mesh_inertia; + mesh.getScaledInertiaTensor(mesh_inertia, scale, density); + + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + // only verify the non-small elements + if (analytic_inertia.mMatrix[i][j] > SMALL_RELATIVE_ERROR) + { + error = fabs(1.f - mesh_inertia.mMatrix[i][j] / analytic_inertia.mMatrix[i][j]); + ensure("LLPrimMassProperties::setUnitMesh() inertia ", error < SMALL_RELATIVE_ERROR); + } + } + } + } + } + + template<> template<> + void mass_properties::test<4>() + { + // test tetrahedron utilities + + // from the paper described here: + // from the numerical example in this paper: + // http://www.journaldatabase.org/articles/87064/Explicit_Exact_Formulas_f.html + + // initialize info about the tetrahedron + std::vector< LLVector3 > points; + points.push_back( LLVector3( 8.33220f, -11.86875f, 0.93355f) ); + points.push_back( LLVector3( 0.75523f, 5.00000f, 16.37072f) ); + points.push_back( LLVector3( 52.61236f, 5.00000f, - 5.38580f) ); + points.push_back( LLVector3( 2.00000f, 5.00000f, 3.00000f) ); + + LLVector3 expected_center( 15.92492f, 0.78281f, 3.732962f); + + LLMatrix3 expected_inertia; + expected_inertia.mMatrix[0][0] = 43520.33257f; + expected_inertia.mMatrix[1][1] = 194711.28938f; + expected_inertia.mMatrix[2][2] = 191168.76173f; + + expected_inertia.mMatrix[0][1] = -11996.20119f; + expected_inertia.mMatrix[1][0] = -11996.20119f; + + expected_inertia.mMatrix[0][2] = 46343.16662f; + expected_inertia.mMatrix[2][0] = 46343.16662f; + + expected_inertia.mMatrix[2][1] = -4417.66150f; + expected_inertia.mMatrix[1][2] = -4417.66150f; + + // measure tetrahedron bounding box max dimension + // for relative error estimates + LLVector3 box_min(FLT_MAX, FLT_MAX, FLT_MAX); + LLVector3 box_max(-FLT_MAX, -FLT_MAX, -FLT_MAX); + for (S32 point_index = 0; point_index < (S32)points.size(); ++point_index) + { + for (S32 i = 0; i < 3; ++i) + { + if (points[point_index].mV[i] < box_min.mV[i]) + { + box_min.mV[i] = points[point_index].mV[i]; + } + if (points[point_index].mV[i] > box_max.mV[i]) + { + box_max.mV[i] = points[point_index].mV[i]; + } + } + } + F32 tetrahedron_max_dimension = (box_max - box_min).length(); + + + // test LLPrimMassProperties::addSignedTetrahedron() + { + LLPrimMassProperties tetrahedron; + tetrahedron.addSignedTetrahedron(1.f, points[0], points[1], points[2], points[3]); + // we must manually center the inertia tensor here + // since addSignedTetrahedron() does not do it automatically + tetrahedron.centerInertiaTensor(); + + // check the center of mass + LLVector3 center = tetrahedron.getCenterOfMass(); + F32 error = (center - expected_center).length() / tetrahedron_max_dimension; + + ensure("LLPrimMassProperties::addSignedTetrahedron() center of mass ", error < SMALL_RELATIVE_ERROR); + + // check the inertia tensor + LLMatrix3 computed_inertia; + LLVector3 scale(1.f, 1.f, 1.f); + F32 density = 1.f; + tetrahedron.getScaledInertiaTensor(computed_inertia, scale, density); + + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + error = fabs(1.f - computed_inertia.mMatrix[i][j] / expected_inertia.mMatrix[i][j]); + ensure("LLPrimMassProperties::addSignedTetrahedron inertia ", error < SMALL_RELATIVE_ERROR); + } + } + } + + // test LLPrimMassProperties::addUnitMesh() + { + std::vector< S32 > triangle_indices; + triangle_indices.push_back(0); + triangle_indices.push_back(2); + triangle_indices.push_back(1); + + triangle_indices.push_back(1); + triangle_indices.push_back(3); + triangle_indices.push_back(0); + + triangle_indices.push_back(2); + triangle_indices.push_back(0); + triangle_indices.push_back(3); + + triangle_indices.push_back(3); + triangle_indices.push_back(1); + triangle_indices.push_back(2); + + LLPrimMassProperties mesh; + mesh.setUnitMesh(points, triangle_indices); + + // check the center of mass + LLVector3 center = mesh.getCenterOfMass(); + F32 error = (center - expected_center).length() / tetrahedron_max_dimension; + + ensure("LLPrimMassProperties::setUnitMesh() center of mass ", error < SMALL_RELATIVE_ERROR); + + // check the inertia tensor + LLMatrix3 computed_inertia; + LLVector3 scale(1.f, 1.f, 1.f); + F32 density = 1.f; + mesh.getScaledInertiaTensor(computed_inertia, scale, density); + + for (S32 i=0; i<3; ++i) + { + for (S32 j=0; j<3; ++j) + { + error = fabs(1.f - computed_inertia.mMatrix[i][j] / expected_inertia.mMatrix[i][j]); + ensure("LLPrimMassProperties::setUnitMesh() inertia diagonal elements mismatch", error < SMALL_RELATIVE_ERROR); + } + } + } + } + + template<> template<> + void mass_properties::test<5>() + { + // test LLPrimMassProperties + + // unit shape box + LLPrimMassProperties box; + box.setUnitBox(); + + // unit shape mesh -- box + + // + // 4-----------0 + // z /| /| + // | / | / | + // | / | / | + // | 6-----------2 | + // | | | | | + // | | 5-------|---1 + // | | / | / + // | | / | / + // | y |/ |/ + // |/ 7-----------3 + // +------------------------ x + + std::vector< LLVector3 > points; + points.push_back( LLVector3( 0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, -0.5f) ); + + std::vector< S32 > triangle_indices; + // +x + triangle_indices.push_back(1); + triangle_indices.push_back(0); + triangle_indices.push_back(2); + + triangle_indices.push_back(1); + triangle_indices.push_back(2); + triangle_indices.push_back(3); + + // -y + triangle_indices.push_back(3); + triangle_indices.push_back(2); + triangle_indices.push_back(7); + + triangle_indices.push_back(7); + triangle_indices.push_back(2); + triangle_indices.push_back(6); + + // -x + triangle_indices.push_back(7); + triangle_indices.push_back(6); + triangle_indices.push_back(4); + + triangle_indices.push_back(7); + triangle_indices.push_back(4); + triangle_indices.push_back(5); + + // +y + triangle_indices.push_back(5); + triangle_indices.push_back(4); + triangle_indices.push_back(1); + + triangle_indices.push_back(1); + triangle_indices.push_back(4); + triangle_indices.push_back(0); + + // +z + triangle_indices.push_back(0); + triangle_indices.push_back(4); + triangle_indices.push_back(6); + + triangle_indices.push_back(0); + triangle_indices.push_back(6); + triangle_indices.push_back(2); + + // -z + triangle_indices.push_back(7); + triangle_indices.push_back(5); + triangle_indices.push_back(3); + + triangle_indices.push_back(3); + triangle_indices.push_back(5); + triangle_indices.push_back(1); + + LLPrimMassProperties mesh; + mesh.setUnitMesh(points, triangle_indices); + + // the unit box and unit mesh mass properties should be nearly the same + + // volume should agree + F32 error = fabs(box.getVolume() - mesh.getVolume()) / box.getVolume(); + ensure("UnitBox and UnitMesh(box) should have same volume", error < SMALL_RELATIVE_ERROR); + + // center of mass should agree + LLVector3 box_center = box.getCenterOfMass(); + LLVector3 mesh_center = mesh.getCenterOfMass(); + error = fabs( (box_center - mesh_center).length() ); + ensure("UnitBox and UnitMesh(box) centers of mass should agree", error < SMALL_RELATIVE_ERROR ); + + LLVector3 scale(1.f, 1.f, 1.f); + F32 density = 1.f; + LLMatrix3 box_inertia, mesh_inertia; + box.getScaledInertiaTensor(box_inertia, scale, density); + mesh.getScaledInertiaTensor(mesh_inertia, scale, density); + + // mesh eigenvalues should be uniform + for (S32 i = 0; i < 2; ++i) + { + error = fabs(mesh_inertia.mMatrix[i][i] - mesh_inertia.mMatrix[i+1][i+1]) / mesh_inertia.mMatrix[i][i]; + ensure("UnitMesh(box) should have uniform eigenvalues", error < SMALL_RELATIVE_ERROR); + } + // inertias should agree + for (S32 i = 0; i < 3; ++i) + { + for (S32 j = 0; j < 3; ++j) + { + error = fabs(box_inertia.mMatrix[i][j] - mesh_inertia.mMatrix[i][j]); + if (error > 0.f + && box_inertia.mMatrix[i][j] != 0.f) + { + error /= box_inertia.mMatrix[i][j]; + } + ensure("UnitBox and UnitMesh(box) should have same inertia", error < SMALL_RELATIVE_ERROR); + } + } + + // Here we test the boundary of the LLPrimLinkInfo::canLink() method + // between semi-random middle-sized objects. + } + + template<> template<> + void mass_properties::test<6>() + { + // test LLObjectMassProperties + + // we make a large single-prim box, then a similarly shaped object + // that is multiple prims, and compare their mass properties + + LLPrimMassProperties box; + box.setUnitBox(); + + F32 density = 3.7f; + LLVector3 big_scale(1.f, 2.f, 3.f); + LLObjectMassProperties big_box(box, big_scale, density); + + LLObjectMassProperties multiple_box; + LLVector3 position; + LLQuaternion rotation; + rotation.loadIdentity(); + + F32 small_box_size = 0.5f; + LLVector3 small_scale( small_box_size, small_box_size, small_box_size); + S32 num_boxes_x = S32(big_scale.mV[VX] / small_box_size); + S32 num_boxes_y = S32(big_scale.mV[VY] / small_box_size); + S32 num_boxes_z = S32(big_scale.mV[VZ] / small_box_size); + LLVector3 start_pos = 0.5f * (small_scale - big_scale); + for (S32 x = 0; x < num_boxes_x; ++x) + { + for (S32 y = 0; y < num_boxes_y; ++y) + { + for (S32 z = 0; z < num_boxes_z; ++z) + { + position.set( F32(x) * small_box_size, F32(y) * small_box_size, F32(z) * small_box_size ); + position += start_pos; + + multiple_box.add(box, small_scale, density, position, rotation); + } + } + } + + // the mass properties of the two boxes should match + + // mass + F32 big_mass = big_box.getMass(); + F32 multiple_mass = multiple_box.getMass(); + F32 error = (big_mass - multiple_mass) / big_mass; + ensure("Big box and equivalent multi-prim box should have same mass", error < SMALL_RELATIVE_ERROR); + + // center of mass + LLVector3 big_center, multiple_center; + big_box.getCenterOfMass(big_center); + multiple_box.getCenterOfMass(multiple_center); + error = (big_center - multiple_center).length(); + ensure("Big box and equivalent multi-prim box should have same center", error < SMALL_RELATIVE_ERROR); + + // inertia + LLMatrix3 big_inertia, multiple_inertia; + big_box.getInertiaLocal(big_inertia); + multiple_box.getInertiaLocal(multiple_inertia); + for (S32 i = 0; i < 3; ++i) + { + for (S32 j = 0; j < 3; ++j) + { + error = fabs(big_inertia.mMatrix[i][j] - multiple_inertia.mMatrix[i][j]); + if (error > 0.f + && big_inertia.mMatrix[i][j] != 0.f) + { + error /= big_inertia.mMatrix[i][j]; + } + ensure("UnitBox and UnitMesh(box) should have same inertia", error < SMALL_RELATIVE_ERROR); + } + } + } + + template<> template<> + void mass_properties::test<7>() + { + // test LLObjectMassProperties with rotations + + // we make a large single-prim box via mesh, then a similarly shaped + // object that is multiple prims (implicit boxes), and compare their + // mass properties + + // + // 4-----------0 + // z /| /| + // | / | / | + // | / | / | + // | 6-----------2 | + // | | | | | + // | | 5-------|---1 + // | | / | / + // | | / | / + // | y |/ |/ + // |/ 7-----------3 + // +------------------------ x + + std::vector< LLVector3 > points; + points.push_back( LLVector3( 0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, -0.5f) ); + + std::vector< S32 > triangle_indices; + // +x + triangle_indices.push_back(1); + triangle_indices.push_back(0); + triangle_indices.push_back(2); + + triangle_indices.push_back(1); + triangle_indices.push_back(2); + triangle_indices.push_back(3); + + // -y + triangle_indices.push_back(3); + triangle_indices.push_back(2); + triangle_indices.push_back(7); + + triangle_indices.push_back(7); + triangle_indices.push_back(2); + triangle_indices.push_back(6); + + // -x + triangle_indices.push_back(7); + triangle_indices.push_back(6); + triangle_indices.push_back(4); + + triangle_indices.push_back(7); + triangle_indices.push_back(4); + triangle_indices.push_back(5); + + // +y + triangle_indices.push_back(5); + triangle_indices.push_back(4); + triangle_indices.push_back(1); + + triangle_indices.push_back(1); + triangle_indices.push_back(4); + triangle_indices.push_back(0); + + // +z + triangle_indices.push_back(0); + triangle_indices.push_back(4); + triangle_indices.push_back(6); + + triangle_indices.push_back(0); + triangle_indices.push_back(6); + triangle_indices.push_back(2); + + // -z + triangle_indices.push_back(7); + triangle_indices.push_back(5); + triangle_indices.push_back(3); + + triangle_indices.push_back(3); + triangle_indices.push_back(5); + triangle_indices.push_back(1); + + F32 angle_step = F_PI / (2.f * 3.f); + for (F32 angle = 0.f; angle < 0.51f * F_PI; angle += angle_step) + { + // scale and rotate mesh points + LLVector3 axis(0.f, 0.f, angle); + LLQuaternion mesh_rotation(angle, axis); + LLVector3 big_scale(3.f, 5.f, 7.f); + std::vector< LLVector3 > new_points; + for (S32 p = 0; p < (S32)points.size(); ++p) + { + LLVector3 new_point = points[p]; + for (S32 i = 0; i < 3; ++i) + { + new_point.mV[i] *= big_scale.mV[i]; + } + new_points.push_back( new_point * mesh_rotation ); + } + + // build the big mesh box + LLPrimMassProperties mesh_box; + mesh_box.setUnitMesh(new_points, triangle_indices); + + F32 density = 3.7f; + LLVector3 unit_scale(1.f, 1.f, 1.f); + LLObjectMassProperties big_box(mesh_box, unit_scale, density); + + // build the multiple_box + LLPrimMassProperties box; + box.setUnitBox(); + + LLObjectMassProperties multiple_box; + LLVector3 position; + + F32 small_box_size = 0.5f; + LLVector3 small_scale( small_box_size, small_box_size, small_box_size); + S32 num_boxes_x = S32(big_scale.mV[VX] / small_box_size); + S32 num_boxes_y = S32(big_scale.mV[VY] / small_box_size); + S32 num_boxes_z = S32(big_scale.mV[VZ] / small_box_size); + LLVector3 start_pos = (0.5f * (small_scale - big_scale)) * mesh_rotation; + for (S32 x = 0; x < num_boxes_x; ++x) + { + for (S32 y = 0; y < num_boxes_y; ++y) + { + for (S32 z = 0; z < num_boxes_z; ++z) + { + position.set( F32(x) * small_box_size, F32(y) * small_box_size, F32(z) * small_box_size ); + position *= mesh_rotation; + position += start_pos; + multiple_box.add(box, small_scale, density, position, mesh_rotation); + } + } + } + + // the mass properties of the two boxes should match + + // mass + F32 big_mass = big_box.getMass(); + F32 multiple_mass = multiple_box.getMass(); + F32 error = (big_mass - multiple_mass) / big_mass; + ensure("Big box and equivalent multi-prim box should have same mass", error < SMALL_RELATIVE_ERROR); + + // center of mass + LLVector3 big_center, multiple_center; + big_box.getCenterOfMass(big_center); + multiple_box.getCenterOfMass(multiple_center); + error = (big_center - multiple_center).length(); + ensure("Big box and equivalent multi-prim box should have same center", error < SMALL_RELATIVE_ERROR); + + LLMatrix3 big_inertia, multiple_inertia; + big_box.getInertiaLocal(big_inertia); + multiple_box.getInertiaLocal(multiple_inertia); + + for (S32 i = 0; i < 3; ++i) + { + for (S32 j = 0; j < 3; ++j) + { + error = fabs(big_inertia.mMatrix[i][j] - multiple_inertia.mMatrix[i][j]); + if (error > 0.f + && big_inertia.mMatrix[i][j] > SMALL_RELATIVE_ERROR) + { + error /= big_inertia.mMatrix[i][j]; + } + ensure("UnitBox and UnitMesh(box) should have same inertia", error < SMALL_RELATIVE_ERROR); + } + } + } + } + + template<> template<> + void mass_properties::test<8>() + { + // test LLPhysicsVolumeManager + + // we make a large single-prim box, then a similarly shaped object + // that is multiple prims, and compare their mass properties + + // first we make the single-prim giant + // + // 4-----------0 + // z /| /| + // | / | / | + // | / | / | + // | 6-----------2 | + // | | | | | + // | | 5-------|---1 + // | | / | / + // | | / | / + // | y |/ |/ + // |/ 7-----------3 + // +------------------------ x + + std::vector< LLVector3 > points; + points.push_back( LLVector3( 0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3( 0.5f, -0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, 0.5f, -0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, 0.5f) ); + points.push_back( LLVector3(-0.5f, -0.5f, -0.5f) ); + + std::vector< S32 > triangle_indices; + // +x + triangle_indices.push_back(1); + triangle_indices.push_back(0); + triangle_indices.push_back(2); + + triangle_indices.push_back(1); + triangle_indices.push_back(2); + triangle_indices.push_back(3); + + // -y + triangle_indices.push_back(3); + triangle_indices.push_back(2); + triangle_indices.push_back(7); + + triangle_indices.push_back(7); + triangle_indices.push_back(2); + triangle_indices.push_back(6); + + // -x + triangle_indices.push_back(7); + triangle_indices.push_back(6); + triangle_indices.push_back(4); + + triangle_indices.push_back(7); + triangle_indices.push_back(4); + triangle_indices.push_back(5); + + // +y + triangle_indices.push_back(5); + triangle_indices.push_back(4); + triangle_indices.push_back(1); + + triangle_indices.push_back(1); + triangle_indices.push_back(4); + triangle_indices.push_back(0); + + // +z + triangle_indices.push_back(0); + triangle_indices.push_back(4); + triangle_indices.push_back(6); + + triangle_indices.push_back(0); + triangle_indices.push_back(6); + triangle_indices.push_back(2); + + // -z + triangle_indices.push_back(7); + triangle_indices.push_back(5); + triangle_indices.push_back(3); + + triangle_indices.push_back(3); + triangle_indices.push_back(5); + triangle_indices.push_back(1); + + // scale the mesh points + LLVector3 big_scale(1.f, 2.f, 3.f); + std::vector< LLVector3 > new_points; + for (S32 p = 0; p < (S32)points.size(); ++p) + { + LLVector3 new_point = points[p]; + for (S32 i = 0; i < 3; ++i) + { + new_point.mV[i] *= big_scale.mV[i]; + } + new_points.push_back( new_point ); + } + + // build the big mesh box (primitive) + LLPrimMassProperties mesh_box; + mesh_box.setUnitMesh(new_points, triangle_indices); + + F32 density = DEFAULT_OBJECT_DENSITY; + LLVector3 unit_scale(1.f, 1.f, 1.f); + LLObjectMassProperties big_box(mesh_box, unit_scale, density); + + // build the multi-prim box (object) + S32 TEST_VOLUME_DETAIL = 1; + + LLVolumeParams volume_params; + volume_params.setCube(); + + LLObjectMassProperties multiple_box; + F32 small_box_size = 0.5f; + LLVector3 small_scale( small_box_size, small_box_size, small_box_size); + { + // hijack the volume manager used by LLPrimitive + LLPhysicsVolumeManager* volume_manager = new LLPhysicsVolumeManager(); + //volume_manager->setThreadSafe(false); + LLPrimitive::setVolumeManager(volume_manager); + + std::vector< const LLPrimitive* > prim_list; + + F32 angle = 0.f; + LLVector3 axis(0.f, 0.f, angle); + LLVector3 position; + LLQuaternion rotation(angle, axis); + S32 num_boxes_x = S32(big_scale.mV[VX] / small_box_size); + S32 num_boxes_y = S32(big_scale.mV[VY] / small_box_size); + S32 num_boxes_z = S32(big_scale.mV[VZ] / small_box_size); + + for (S32 x = 0; x < num_boxes_x; ++x) + { + for (S32 y = 0; y < num_boxes_y; ++y) + { + for (S32 z = 0; z < num_boxes_z; ++z) + { + LLPrimitive* primp = new LLPrimitive(); + primp->setVolume( volume_params, TEST_VOLUME_DETAIL); + + position.set( F32(x) * small_box_size, F32(y) * small_box_size, F32(z) * small_box_size ); + position *= rotation; + + primp->setPosition(position); + primp->setRotation(rotation); + primp->setScale(small_scale); + + prim_list.push_back(primp); + } + } + } + + volume_manager->getObjectMassProperties(multiple_box, prim_list); + + for (S32 i = 0; i < (S32)prim_list.size(); ++i) + { + delete prim_list[i]; + prim_list[i] = NULL; + } + LLPrimitive::cleanupVolumeManager(); + } + + // mass + F32 big_mass = big_box.getMass(); + F32 multiple_mass = multiple_box.getMass(); + F32 error = (big_mass - multiple_mass) / big_mass; + ensure("Big box and equivalent multi-prim box should have same mass", error < SMALL_RELATIVE_ERROR); + + // center of mass + LLVector3 big_center, multiple_center; + big_box.getCenterOfMass(big_center); + multiple_box.getCenterOfMass(multiple_center); + LLVector3 expected_shift = 0.5f * ( big_scale - small_scale ); + error = ( big_center - (multiple_center - expected_shift) ).length(); + ensure("Big box and equivalent multi-prim box should have same center", error < SMALL_RELATIVE_ERROR); + + // inertia + LLMatrix3 big_inertia, multiple_inertia; + big_box.getInertiaLocal(big_inertia); + multiple_box.getInertiaLocal(multiple_inertia); + + for (S32 i = 0; i < 3; ++i) + { + for (S32 j = 0; j < 3; ++j) + { + error = fabs(big_inertia.mMatrix[i][j] - multiple_inertia.mMatrix[i][j]); + if (error > 0.f + && big_inertia.mMatrix[i][j] > SMALL_RELATIVE_ERROR) + { + error /= big_inertia.mMatrix[i][j]; + } + bool ok = error < SMALL_RELATIVE_ERROR + || (i != j + && error < SMALL_RELATIVE_ERROR); + ensure("UnitBox and UnitMesh(box) should have same inertia", ok ); + } + } + } +} + diff --git a/linden/indra/test/math.cpp b/linden/indra/test/math.cpp index 9cab3fe..405b8a3 100644 --- a/linden/indra/test/math.cpp +++ b/linden/indra/test/math.cpp @@ -34,9 +34,13 @@ #include "linden_common.h" #include "lltut.h" +#include "llcrc.h" +#include "llline.h" #include "llmath.h" +#include "llrand.h" +#include "llsphere.h" #include "lluuid.h" -#include "llcrc.h" +#include "v3math.h" namespace tut { @@ -277,3 +281,439 @@ namespace tut ensure_equals("crc update 2", c1.getCRC(), c2.getCRC()); } } + +namespace tut +{ + struct sphere_data + { + }; + typedef test_group sphere_test; + typedef sphere_test::object sphere_object; + tut::sphere_test tsphere("LLSphere"); + + template<> template<> + void sphere_object::test<1>() + { + // test LLSphere::contains() and ::overlaps() + S32 number_of_tests = 10; + for (S32 test = 0; test < number_of_tests; ++test) + { + LLVector3 first_center(1.f, 1.f, 1.f); + F32 first_radius = 3.f; + LLSphere first_sphere( first_center, first_radius ); + + F32 half_millimeter = 0.0005f; + LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + direction.normalize(); + + F32 distance = ll_frand(first_radius - 2.f * half_millimeter); + LLVector3 second_center = first_center + distance * direction; + F32 second_radius = first_radius - distance - half_millimeter; + LLSphere second_sphere( second_center, second_radius ); + ensure("first sphere should contain the second", first_sphere.contains(second_sphere)); + ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere)); + + distance = first_radius + ll_frand(first_radius); + second_center = first_center + distance * direction; + second_radius = distance - first_radius + half_millimeter; + second_sphere.set( second_center, second_radius ); + ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere)); + ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere)); + + distance = first_radius + ll_frand(first_radius) + half_millimeter; + second_center = first_center + distance * direction; + second_radius = distance - first_radius - half_millimeter; + second_sphere.set( second_center, second_radius ); + ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere)); + ensure("first sphere should NOT overlap the second", !first_sphere.overlaps(second_sphere)); + } + } + + template<> template<> + void sphere_object::test<2>() + { + // test LLSphere::getBoundingSphere() + S32 number_of_tests = 100; + S32 number_of_spheres = 10; + F32 sphere_center_range = 32.f; + F32 sphere_radius_range = 5.f; + + for (S32 test = 0; test < number_of_tests; ++test) + { + // gegnerate a bunch of random sphere + std::vector< LLSphere > sphere_list; + for (S32 sphere_count=0; sphere_count < number_of_spheres; ++sphere_count) + { + LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + direction.normalize(); + F32 distance = ll_frand(sphere_center_range); + LLVector3 center = distance * direction; + F32 radius = ll_frand(sphere_radius_range); + LLSphere sphere( center, radius ); + sphere_list.push_back(sphere); + } + + // compute the bounding sphere + LLSphere bounding_sphere = LLSphere::getBoundingSphere(sphere_list); + + // make sure all spheres are inside the bounding sphere + { + std::vector< LLSphere >::const_iterator sphere_itr; + for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) + { + ensure("sphere should be contained by the bounding sphere", bounding_sphere.contains(*sphere_itr)); + } + } + + // TODO -- improve LLSphere::getBoundingSphere() to the point where + // we can reduce the 'expansion' in the two tests below to about + // 2 mm or less + + F32 expansion = 0.005f; + // move all spheres out a little bit + // and count how many are NOT contained + { + std::vector< LLVector3 > uncontained_directions; + std::vector< LLSphere >::iterator sphere_itr; + for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) + { + LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter(); + direction.normalize(); + + sphere_itr->setCenter( sphere_itr->getCenter() + expansion * direction ); + if (! bounding_sphere.contains( *sphere_itr ) ) + { + uncontained_directions.push_back(direction); + } + } + ensure("when moving spheres out there should be at least two uncontained spheres", + uncontained_directions.size() > 1); + + /* TODO -- when the bounding sphere algorithm is improved we can open up this test + * at the moment it occasionally fails when the sphere collection is tight and small + * (2 meters or less) + if (2 == uncontained_directions.size() ) + { + // if there were only two uncontained spheres then + // the two directions should be nearly opposite + F32 dir_dot = uncontained_directions[0] * uncontained_directions[1]; + ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f); + } + */ + } + + // compute the new bounding sphere + bounding_sphere = LLSphere::getBoundingSphere(sphere_list); + + // increase the size of all spheres a little bit + // and count how many are NOT contained + { + std::vector< LLVector3 > uncontained_directions; + std::vector< LLSphere >::iterator sphere_itr; + for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr) + { + LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter(); + direction.normalize(); + + sphere_itr->setRadius( sphere_itr->getRadius() + expansion ); + if (! bounding_sphere.contains( *sphere_itr ) ) + { + uncontained_directions.push_back(direction); + } + } + ensure("when boosting sphere radii there should be at least two uncontained spheres", + uncontained_directions.size() > 1); + + /* TODO -- when the bounding sphere algorithm is improved we can open up this test + * at the moment it occasionally fails when the sphere collection is tight and small + * (2 meters or less) + if (2 == uncontained_directions.size() ) + { + // if there were only two uncontained spheres then + // the two directions should be nearly opposite + F32 dir_dot = uncontained_directions[0] * uncontained_directions[1]; + ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f); + } + */ + } + } + } +} + +namespace tut +{ + F32 SMALL_RADIUS = 1.0f; + F32 MEDIUM_RADIUS = 5.0f; + F32 LARGE_RADIUS = 10.0f; + + struct line_data + { + }; + typedef test_group line_test; + typedef line_test::object line_object; + tut::line_test tline("LLLine"); + + template<> template<> + void line_object::test<1>() + { + // this is a test for LLLine::intersects(point) which returns TRUE + // if the line passes within some tolerance of point + + // these tests will have some floating point error, + // so we need to specify how much error is ok + F32 allowable_relative_error = 0.00001f; + S32 number_of_tests = 100; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate some random point to be on the line + LLVector3 point_on_line( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_line.normalize(); + point_on_line *= ll_frand(LARGE_RADIUS); + + // generate some random point to "intersect" + LLVector3 random_direction ( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + random_direction.normalize(); + + LLVector3 random_offset( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + random_offset.normalize(); + random_offset *= ll_frand(SMALL_RADIUS); + + LLVector3 point = point_on_line + MEDIUM_RADIUS * random_direction + + random_offset; + + // compute the axis of approach (a unit vector between the points) + LLVector3 axis_of_approach = point - point_on_line; + axis_of_approach.normalize(); + + // compute the direction of the the first line (perp to axis_of_approach) + LLVector3 first_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + first_dir.normalize(); + F32 dot = first_dir * axis_of_approach; + first_dir -= dot * axis_of_approach; // subtract component parallel to axis + first_dir.normalize(); + + // construct the line + LLVector3 another_point_on_line = point_on_line + ll_frand(LARGE_RADIUS) * first_dir; + LLLine line(another_point_on_line, point_on_line); + + // test that the intersection point is within MEDIUM_RADIUS + SMALL_RADIUS + F32 test_radius = MEDIUM_RADIUS + SMALL_RADIUS; + test_radius += (LARGE_RADIUS * allowable_relative_error); + ensure("line should pass near intersection point", line.intersects(point, test_radius)); + + test_radius = allowable_relative_error * (point - point_on_line).length(); + ensure("line should intersect point used to define it", line.intersects(point_on_line, test_radius)); + } + } + + template<> template<> + void line_object::test<2>() + { + // this is a test for LLLine::nearestApproach(LLLIne) method + // which computes the point on a line nearest another line + + // these tests will have some floating point error, + // so we need to specify how much error is ok + // TODO -- make nearestApproach() algorithm more accurate so + // we can tighten the allowable_error. Most tests are tighter + // than one milimeter, however when doing randomized testing + // you can walk into inaccurate cases. + F32 allowable_relative_error = 0.001f; + S32 number_of_tests = 100; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate two points to be our known nearest approaches + LLVector3 some_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + some_point.normalize(); + some_point *= ll_frand(LARGE_RADIUS); + + LLVector3 another_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + another_point.normalize(); + another_point *= ll_frand(LARGE_RADIUS); + + // compute the axis of approach (a unit vector between the points) + LLVector3 axis_of_approach = another_point - some_point; + axis_of_approach.normalize(); + + // compute the direction of the the first line (perp to axis_of_approach) + LLVector3 first_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + F32 dot = first_dir * axis_of_approach; + first_dir -= dot * axis_of_approach; // subtract component parallel to axis + first_dir.normalize(); // normalize + + // compute the direction of the the second line + LLVector3 second_dir( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + dot = second_dir * axis_of_approach; + second_dir -= dot * axis_of_approach; + second_dir.normalize(); + + // make sure the lines aren't too parallel, + dot = fabsf(first_dir * second_dir); + if (dot > 0.99f) + { + // skip this test, we're not interested in testing + // the intractible cases + continue; + } + + // construct the lines + LLVector3 first_point = some_point + ll_frand(LARGE_RADIUS) * first_dir; + LLLine first_line(first_point, some_point); + + LLVector3 second_point = another_point + ll_frand(LARGE_RADIUS) * second_dir; + LLLine second_line(second_point, another_point); + + // compute the points of nearest approach + LLVector3 some_computed_point = first_line.nearestApproach(second_line); + LLVector3 another_computed_point = second_line.nearestApproach(first_line); + + // compute the error + F32 first_error = (some_point - some_computed_point).length(); + F32 scale = llmax((some_point - another_point).length(), some_point.length()); + scale = llmax(scale, another_point.length()); + scale = llmax(scale, 1.f); + F32 first_relative_error = first_error / scale; + + F32 second_error = (another_point - another_computed_point).length(); + F32 second_relative_error = second_error / scale; + + //if (first_relative_error > allowable_relative_error) + //{ + // std::cout << "first_error = " << first_error + // << " first_relative_error = " << first_relative_error + // << " scale = " << scale + // << " dir_dot = " << (first_dir * second_dir) + // << std::endl; + //} + //if (second_relative_error > allowable_relative_error) + //{ + // std::cout << "second_error = " << second_error + // << " second_relative_error = " << second_relative_error + // << " scale = " << scale + // << " dist = " << (some_point - another_point).length() + // << " dir_dot = " << (first_dir * second_dir) + // << std::endl; + //} + + // test that the errors are small + ensure("first line should accurately compute its closest approach", + first_relative_error <= allowable_relative_error); + ensure("second line should accurately compute its closest approach", + second_relative_error <= allowable_relative_error); + } + } + + F32 ALMOST_PARALLEL = 0.99f; + template<> template<> + void line_object::test<3>() + { + // this is a test for LLLine::getIntersectionBetweenTwoPlanes() method + + // first some known tests + LLLine xy_plane(LLVector3(0.f, 0.f, 2.f), LLVector3(0.f, 0.f, 3.f)); + LLLine yz_plane(LLVector3(2.f, 0.f, 0.f), LLVector3(3.f, 0.f, 0.f)); + LLLine zx_plane(LLVector3(0.f, 2.f, 0.f), LLVector3(0.f, 3.f, 0.f)); + + LLLine x_line; + LLLine y_line; + LLLine z_line; + + bool x_success = LLLine::getIntersectionBetweenTwoPlanes(x_line, xy_plane, zx_plane); + bool y_success = LLLine::getIntersectionBetweenTwoPlanes(y_line, yz_plane, xy_plane); + bool z_success = LLLine::getIntersectionBetweenTwoPlanes(z_line, zx_plane, yz_plane); + + ensure("xy and zx planes should intersect", x_success); + ensure("yz and xy planes should intersect", y_success); + ensure("zx and yz planes should intersect", z_success); + + LLVector3 direction = x_line.getDirection(); + ensure("x_line should be parallel to x_axis", fabs(direction.mV[VX]) == 1.f + && 0.f == direction.mV[VY] + && 0.f == direction.mV[VZ] ); + direction = y_line.getDirection(); + ensure("y_line should be parallel to y_axis", 0.f == direction.mV[VX] + && fabs(direction.mV[VY]) == 1.f + && 0.f == direction.mV[VZ] ); + direction = z_line.getDirection(); + ensure("z_line should be parallel to z_axis", 0.f == direction.mV[VX] + && 0.f == direction.mV[VY] + && fabs(direction.mV[VZ]) == 1.f ); + + // next some random tests + F32 allowable_relative_error = 0.0001f; + S32 number_of_tests = 20; + for (S32 test = 0; test < number_of_tests; ++test) + { + // generate the known line + LLVector3 some_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + some_point.normalize(); + some_point *= ll_frand(LARGE_RADIUS); + LLVector3 another_point( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + another_point.normalize(); + another_point *= ll_frand(LARGE_RADIUS); + LLLine known_intersection(some_point, another_point); + + // compute a plane that intersect the line + LLVector3 point_on_plane( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_plane.normalize(); + point_on_plane *= ll_frand(LARGE_RADIUS); + LLVector3 plane_normal = (point_on_plane - some_point) % known_intersection.getDirection(); + plane_normal.normalize(); + LLLine first_plane(point_on_plane, point_on_plane + plane_normal); + + // compute a different plane that intersect the line + LLVector3 point_on_different_plane( ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f, + ll_frand(2.f) - 1.f); + point_on_different_plane.normalize(); + point_on_different_plane *= ll_frand(LARGE_RADIUS); + LLVector3 different_plane_normal = (point_on_different_plane - another_point) % known_intersection.getDirection(); + different_plane_normal.normalize(); + LLLine second_plane(point_on_different_plane, point_on_different_plane + different_plane_normal); + + if (fabs(plane_normal * different_plane_normal) > ALMOST_PARALLEL) + { + // the two planes are approximately parallel, so we won't test this case + continue; + } + + LLLine measured_intersection; + bool success = LLLine::getIntersectionBetweenTwoPlanes( + measured_intersection, + first_plane, + second_plane); + + ensure("plane intersection should succeed", success); + + F32 dot = fabs(known_intersection.getDirection() * measured_intersection.getDirection()); + ensure("measured intersection should be parallel to known intersection", + dot > ALMOST_PARALLEL); + + ensure("measured intersection should pass near known point", + measured_intersection.intersects(some_point, LARGE_RADIUS * allowable_relative_error)); + } + } +} + diff --git a/linden/indra/test/message_tut.cpp b/linden/indra/test/message_tut.cpp index 60a249c..b619d38 100644 --- a/linden/indra/test/message_tut.cpp +++ b/linden/indra/test/message_tut.cpp @@ -62,7 +62,7 @@ namespace tut if(! init) { ll_init_apr(); - init_prehash_data(); + //init_prehash_data(); init = true; } diff --git a/linden/indra/test/prim_linkability_tut.cpp b/linden/indra/test/prim_linkability_tut.cpp new file mode 100644 index 0000000..c5f2958 --- /dev/null +++ b/linden/indra/test/prim_linkability_tut.cpp @@ -0,0 +1,490 @@ +/** + * @file linkability.cpp + * @author andrew@lindenlab.com + * @date 2007-04-23 + * @brief Tests for the LLPrimLinkInfo template which computes the linkability of prims + * + * $LicenseInfo:firstyear=2007&license=internal$ + * + * Copyright (c) 2007-2008, Linden Research, Inc. + * + * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of + * this source code is governed by the Linden Lab Source Code Disclosure + * Agreement ("Agreement") previously entered between you and Linden + * Lab. By accessing, using, copying, modifying or distributing this + * software, you acknowledge that you have been informed of your + * obligations under the Agreement and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lltut.h" +#include "llprimlinkinfo.h" +#include "llrand.h" + + +// helper function +void randomize_sphere(LLSphere& sphere, F32 center_range, F32 radius_range) +{ + F32 radius = ll_frand(2.f * radius_range) - radius_range; + LLVector3 center; + for (S32 i=0; i<3; ++i) + { + center.mV[i] = ll_frand(2.f * center_range) - center_range; + } + sphere.setRadius(radius); + sphere.setCenter(center); +} + +// helper function. Same as above with a min and max radius. +void randomize_sphere(LLSphere& sphere, F32 center_range, F32 minimum_radius, F32 maximum_radius) +{ + F32 radius = ll_frand(maximum_radius - minimum_radius) + minimum_radius; + LLVector3 center; + for (S32 i=0; i<3; ++i) + { + center.mV[i] = ll_frand(2.f * center_range) - center_range; + } + sphere.setRadius(radius); + sphere.setCenter(center); +} + +// helper function +bool random_sort( const LLPrimLinkInfo< S32 >&, const LLPrimLinkInfo< S32 >& b) +{ + return (ll_rand(64) < 32); +} + +namespace tut +{ + struct linkable_data + { + LLPrimLinkInfo info; + }; + + typedef test_group linkable_test; + typedef linkable_test::object linkable_object; + tut::linkable_test wtf("prim linkability"); + + template<> template<> + void linkable_object::test<1>() + { + // Here we test the boundary of the LLPrimLinkInfo::canLink() method + // between semi-random middle-sized objects. + + S32 number_of_tests = 100; + for (S32 test = 0; test < number_of_tests; ++test) + { + // compute the radii that would provide the above max link distance + F32 first_radius = 0.f; + F32 second_radius = 0.f; + + // compute a random center for the first sphere + // compute some random max link distance + F32 max_link_span = ll_frand(MAX_OBJECT_SPAN); + if (max_link_span < OBJECT_SPAN_BONUS) + { + max_link_span += OBJECT_SPAN_BONUS; + } + LLVector3 first_center( + ll_frand(2.f * max_link_span) - max_link_span, + ll_frand(2.f * max_link_span) - max_link_span, + ll_frand(2.f * max_link_span) - max_link_span); + + // put the second sphere at the right distance from the origin + // such that it is within the max_link_distance of the first + LLVector3 direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + direction.normalize(); + F32 half_milimeter = 0.0005f; + LLVector3 second_center; + + // max_span = 3 * (first_radius + second_radius) + OBJECT_SPAN_BONUS + // make sure they link at short distances + { + second_center = first_center + (OBJECT_SPAN_BONUS - half_milimeter) * direction; + LLPrimLinkInfo first_info(0, LLSphere(first_center, first_radius) ); + LLPrimLinkInfo second_info(1, LLSphere(second_center, second_radius) ); + ensure("these nearby objects should link", first_info.canLink(second_info) ); + } + + // make sure they fail to link if we move them apart just a little bit + { + second_center = first_center + (OBJECT_SPAN_BONUS + half_milimeter) * direction; + LLPrimLinkInfo first_info(0, LLSphere(first_center, first_radius) ); + LLPrimLinkInfo second_info(1, LLSphere(second_center, second_radius) ); + ensure("these nearby objects should NOT link", !first_info.canLink(second_info) ); + } + + // make sure the objects link or not at medium distances + { + first_radius = 0.3f * ll_frand(max_link_span - OBJECT_SPAN_BONUS); + + // This is the exact second radius that will link at exactly our random max_link_distance + second_radius = ((max_link_span - OBJECT_SPAN_BONUS) / 3.f) - first_radius; + second_center = first_center + (max_link_span - first_radius - second_radius - half_milimeter) * direction; + + LLPrimLinkInfo first_info(0, LLSphere(first_center, first_radius) ); + LLPrimLinkInfo second_info(1, LLSphere(second_center, second_radius) ); + + ensure("these objects should link", first_info.canLink(second_info) ); + } + + // make sure they fail to link if we move them apart just a little bit + { + // move the second sphere such that it is a little too far from the first + second_center += (2.f * half_milimeter) * direction; + LLPrimLinkInfo first_info(0, LLSphere(first_center, first_radius) ); + LLPrimLinkInfo second_info(1, LLSphere(second_center, second_radius) ); + + ensure("these objects should NOT link", !first_info.canLink(second_info) ); + } + + // make sure things don't link at far distances + { + second_center = first_center + (MAX_OBJECT_SPAN + 2.f * half_milimeter) * direction; + second_radius = 0.3f * MAX_OBJECT_SPAN; + LLPrimLinkInfo first_info(0, LLSphere(first_center, first_radius) ); + LLPrimLinkInfo second_info(1, LLSphere(second_center, second_radius) ); + ensure("these objects should NOT link", !first_info.canLink(second_info) ); + } + + } + } + + template<> template<> + void linkable_object::test<2>() + { + + // Consider a row of eight spheres in a row, each 10m in diameter and centered + // at 10m intervals: 01234567. + + F32 radius = 5.f; + F32 spacing = 10.f; + + LLVector3 line_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + line_direction.normalize(); + + LLVector3 first_center(ll_frand(2.f * spacing) -spacing, ll_frand(2.f * spacing) - spacing, ll_frand(2.f * spacing) - spacing); + + LLPrimLinkInfo infos[8]; + + for (S32 index = 0; index < 8; ++index) + { + LLVector3 center = first_center + ((F32)(index) * spacing) * line_direction; + infos[index].set(index, LLSphere(center, radius)); + } + + // Max span for 2 spheres of 5m radius is 3 * (5 + 5) + 1 = 31m + // spheres 0&2 have a 30m span (from outside edge to outside edge) and should link + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + info_list.push_back(infos[2]); + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("0&2 prim count should be 2", prim_count, 2); + ensure_equals("0&2 unlinkable list should have length 0", (S32) info_list.size(), 0); + } + + + // spheres 0&3 have a 40 meter span and should NOT link outright + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + info_list.push_back(infos[3]); + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("0&4 prim count should be 1", prim_count, 1); + ensure_equals("0&4 unlinkable list should have length 1", (S32) info_list.size(), 1); + } + + + // spheres 0-4 should link no matter what order : 01234 + // Total span = 50m, 012 link with a r=15.5 giving max span of 3 * (15.5 + 5) + 1 = 62.5, but the cap is 54m + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + for (S32 index = 1; index < 5; ++index) + { + info_list.push_back(infos[index]); + } + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("01234 prim count should be 5", prim_count, 5); + ensure_equals("01234 unlinkable list should have length 0", (S32) info_list.size(), 0); + } + + + // spheres 0-5 should link no matter what order : 04321 + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + for (S32 index = 4; index > 0; --index) + { + info_list.push_back(infos[index]); + } + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("04321 prim count should be 5", prim_count, 5); + ensure_equals("04321 unlinkable list should have length 0", (S32) info_list.size(), 0); + } + + // spheres 0-4 should link no matter what order : 01423 + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + info_list.push_back(infos[1]); + info_list.push_back(infos[4]); + info_list.push_back(infos[2]); + info_list.push_back(infos[3]); + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("01423 prim count should be 5", prim_count, 5); + ensure_equals("01423 unlinkable list should have length 0", (S32) info_list.size(), 0); + } + + // spheres 0-5 should NOT fully link, only 0-4 + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + for (S32 index = 1; index < 6; ++index) + { + info_list.push_back(infos[index]); + } + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("012345 prim count should be 5", prim_count, 5); + ensure_equals("012345 unlinkable list should have length 1", (S32) info_list.size(), 1); + std::list< LLPrimLinkInfo >::iterator info_itr = info_list.begin(); + if (info_itr != info_list.end()) + { + // examine the contents of the unlinked info + std::list unlinked_indecies; + info_itr->getData(unlinked_indecies); + // make sure there is only one index in the unlinked_info + ensure_equals("012345 unlinkable index count should be 1", (S32) unlinked_indecies.size(), 1); + // make sure its value is 6 + std::list::iterator unlinked_index_itr = unlinked_indecies.begin(); + S32 unlinkable_index = *unlinked_index_itr; + ensure_equals("012345 unlinkable index should be 5", (S32) unlinkable_index, 5); + } + } + + // spheres 0-7 should NOT fully link, only 0-5 + { + LLPrimLinkInfo root_info = infos[0]; + std::list< LLPrimLinkInfo > info_list; + for (S32 index = 1; index < 8; ++index) + { + info_list.push_back(infos[index]); + } + root_info.mergeLinkableSet(info_list); + S32 prim_count = root_info.getPrimCount(); + ensure_equals("01234567 prim count should be 5", prim_count, 5); + // Should be 1 linkinfo on unlinkable that has 2 prims + ensure_equals("01234567 unlinkable list should have length 1", (S32) info_list.size(), 1); + std::list< LLPrimLinkInfo >::iterator info_itr = info_list.begin(); + if (info_itr != info_list.end()) + { + // make sure there is only one index in the unlinked_info + std::list unlinked_indecies; + info_itr->getData(unlinked_indecies); + ensure_equals("0123456 unlinkable index count should be 3", (S32) unlinked_indecies.size(), 3); + + // make sure its values are 6 and 7 + std::list::iterator unlinked_index_itr = unlinked_indecies.begin(); + S32 unlinkable_index = *unlinked_index_itr; + ensure_equals("0123456 first unlinkable index should be 5", (S32) unlinkable_index, 5); + ++unlinked_index_itr; + unlinkable_index = *unlinked_index_itr; + ensure_equals("0123456 second unlinkable index should be 6", (S32) unlinkable_index, 6); + ++unlinked_index_itr; + unlinkable_index = *unlinked_index_itr; + ensure_equals("0123456 third unlinkable index should be 7", (S32) unlinkable_index, 7); + + } + } + } + + template<> template<> + void linkable_object::test<3>() + { + // Here we test the link results between an LLPrimLinkInfo and a set of + // randomized LLPrimLinkInfos where the expected results are known. + S32 number_of_tests = 5; + for (S32 test = 0; test < number_of_tests; ++test) + { + // the radii are known + F32 first_radius = 1.f; + F32 second_radius = 2.f; + F32 third_radius = 3.f; + + // compute the distances + F32 half_milimeter = 0.0005f; + F32 max_first_second_span = 3.f * (first_radius + second_radius) + OBJECT_SPAN_BONUS; + F32 linkable_distance = max_first_second_span - first_radius - second_radius - half_milimeter; + + F32 max_full_span = 3.f * (0.5f * max_first_second_span + third_radius) + OBJECT_SPAN_BONUS; + F32 unlinkable_distance = max_full_span - 0.5f * linkable_distance - third_radius + half_milimeter; + + // compute some random directions + LLVector3 first_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + first_direction.normalize(); + LLVector3 second_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + second_direction.normalize(); + LLVector3 third_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f); + third_direction.normalize(); + + // compute the centers + LLVector3 first_center = ll_frand(10.f) * first_direction; + LLVector3 second_center = first_center + ll_frand(linkable_distance) * second_direction; + LLVector3 first_join_center = 0.5f * (first_center + second_center); + LLVector3 third_center = first_join_center + unlinkable_distance * third_direction; + + // make sure the second info links and the third does not + { + // initialize the infos + S32 index = 0; + LLPrimLinkInfo first_info(index++, LLSphere(first_center, first_radius)); + LLPrimLinkInfo second_info(index++, LLSphere(second_center, second_radius)); + LLPrimLinkInfo third_info(index++, LLSphere(third_center, third_radius)); + + // put the second and third infos in a list + std::list< LLPrimLinkInfo > info_list; + info_list.push_back(second_info); + info_list.push_back(third_info); + + // merge the list with the first_info + first_info.mergeLinkableSet(info_list); + S32 prim_count = first_info.getPrimCount(); + + ensure_equals("prim count should be 2", prim_count, 2); + ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1); + } + + // reverse the order and make sure we get the same results + { + // initialize the infos + S32 index = 0; + LLPrimLinkInfo first_info(index++, LLSphere(first_center, first_radius)); + LLPrimLinkInfo second_info(index++, LLSphere(second_center, second_radius)); + LLPrimLinkInfo third_info(index++, LLSphere(third_center, third_radius)); + + // build the list in the reverse order + std::list< LLPrimLinkInfo > info_list; + info_list.push_back(third_info); + info_list.push_back(second_info); + + // merge the list with the first_info + first_info.mergeLinkableSet(info_list); + S32 prim_count = first_info.getPrimCount(); + + ensure_equals("prim count should be 2", prim_count, 2); + ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1); + } + } + } + + template<> template<> + void linkable_object::test<4>() + { + // Here we test whether linkability is invarient under permutations + // of link order. To do this we generate a bunch of random spheres + // and then try to link them in different ways. + // + // NOTE: the linkability will only be invarient if there is only one + // linkable solution. Multiple solutions will exist if the set of + // candidates are larger than the maximum linkable distance, or more + // numerous than a single linked object can contain. This is easily + // understood by considering a very large set of link candidates, + // and first linking preferentially to the left until linking fails, + // then doing the same to the right -- the final solutions will differ. + // Hence for this test we must generate candidate sets that lie within + // the linkability envelope of a single object. + // + // NOTE: a random set of objects will tend to either be totally linkable + // or totally not. That is, the random orientations that + + F32 root_center_range = 0.f; + F32 min_prim_radius = 0.1f; + F32 max_prim_radius = 2.f; + + // Linkability is min(MAX_OBJECT_SPAN,3 *( R1 + R2 ) + BONUS) + // 3 * (min_prim_radius + min_prim_radius) + OBJECT_SPAN_BONUS = 6 * min_prim_radius + OBJECT_SPAN_BONUS; + // Use .45 instead of .5 to gaurantee objects are within the minimum span. + F32 child_center_range = 0.45f * ( (6*min_prim_radius) + OBJECT_SPAN_BONUS ); + + S32 number_of_tests = 100; + S32 number_of_spheres = 10; + S32 number_of_scrambles = 10; + S32 number_of_random_bubble_sorts = 10; + + for (S32 test = 0; test < number_of_tests; ++test) + { + LLSphere sphere; + S32 sphere_index = 0; + + // build the root piece + randomize_sphere(sphere, root_center_range, min_prim_radius, max_prim_radius); + info.set( sphere_index++, sphere ); + + // build the unlinked pieces + std::list< LLPrimLinkInfo > info_list; + for (; sphere_index < number_of_spheres; ++sphere_index) + { + randomize_sphere(sphere, child_center_range, min_prim_radius, max_prim_radius); + LLPrimLinkInfo child_info( sphere_index, sphere ); + info_list.push_back(child_info); + } + + // declare the variables used to store the results + std::list first_linked_list; + + { + // the link attempt will modify our original info's, so we + // have to make copies of the originals for testing + LLPrimLinkInfo test_info( 0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) ); + std::list< LLPrimLinkInfo > test_list; + test_list.assign(info_list.begin(), info_list.end()); + + // try to link + test_info.mergeLinkableSet(test_list); + + ensure("All prims should link, but did not.",test_list.empty()); + + // store the results + test_info.getData(first_linked_list); + first_linked_list.sort(); + } + + // try to link the spheres in various random orders + for (S32 scramble = 0; scramble < number_of_scrambles; ++scramble) + { + LLPrimLinkInfo test_info(0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) ); + + // scramble the order of the info_list + std::list< LLPrimLinkInfo > test_list; + test_list.assign(info_list.begin(), info_list.end()); + for (S32 i = 0; i < number_of_random_bubble_sorts; i++) + { + test_list.sort(random_sort); + } + + // try to link + test_info.mergeLinkableSet(test_list); + + // get the results + std::list linked_list; + test_info.getData(linked_list); + linked_list.sort(); + + ensure_equals("linked set size should be order independent",linked_list.size(),first_linked_list.size()); + } + } + } +} + diff --git a/linden/indra/test/test.vcproj b/linden/indra/test/test.vcproj index 4476c2e..f720d9a 100644 --- a/linden/indra/test/test.vcproj +++ b/linden/indra/test/test.vcproj @@ -20,7 +20,7 @@ + + + + +