diff options
Diffstat (limited to 'indra/newview/tests')
-rw-r--r-- | indra/newview/tests/llagentaccess_test.cpp | 2 | ||||
-rw-r--r-- | indra/newview/tests/llcapabilitylistener_test.cpp | 37 | ||||
-rw-r--r-- | indra/newview/tests/lldateutil_test.cpp | 39 | ||||
-rw-r--r-- | indra/newview/tests/lllogininstance_test.cpp | 424 | ||||
-rw-r--r-- | indra/newview/tests/llmediadataclient_test.cpp | 938 | ||||
-rw-r--r-- | indra/newview/tests/lltextureinfo_test.cpp | 284 | ||||
-rw-r--r-- | indra/newview/tests/lltextureinfodetails_test.cpp | 98 | ||||
-rw-r--r-- | indra/newview/tests/lltexturestatsuploader_test.cpp | 156 | ||||
-rw-r--r-- | indra/newview/tests/llviewerhelputil_test.cpp | 128 | ||||
-rw-r--r-- | indra/newview/tests/llworldmap_test.cpp | 523 | ||||
-rw-r--r-- | indra/newview/tests/llworldmipmap_test.cpp | 176 | ||||
-rw-r--r-- | indra/newview/tests/llxmlrpclistener_test.cpp | 230 | ||||
-rw-r--r-- | indra/newview/tests/test_llxmlrpc_peer.py | 59 |
13 files changed, 3020 insertions, 74 deletions
diff --git a/indra/newview/tests/llagentaccess_test.cpp b/indra/newview/tests/llagentaccess_test.cpp index 42872d85fb..e08193f785 100644 --- a/indra/newview/tests/llagentaccess_test.cpp +++ b/indra/newview/tests/llagentaccess_test.cpp @@ -29,6 +29,8 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" #include "../test/lltut.h" #include "../llagentaccess.h" diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp index bd6ae90fab..ca7d02fc68 100644 --- a/indra/newview/tests/llcapabilitylistener_test.cpp +++ b/indra/newview/tests/llcapabilitylistener_test.cpp @@ -47,9 +47,10 @@ #include "../test/lltut.h" #include "../llcapabilityprovider.h" #include "lluuid.h" -#include "llerrorcontrol.h" #include "tests/networkio.h" #include "tests/commtest.h" +#include "tests/wrapllerrs.h" +#include "message.h" #include "stringize.h" #if defined(LL_WINDOWS) @@ -127,28 +128,6 @@ namespace tut typedef llcapears_group::object llcapears_object; llcapears_group llsdmgr("llcapabilitylistener"); - struct CaptureError: public LLError::OverrideFatalFunction - { - CaptureError(): - LLError::OverrideFatalFunction(boost::bind(&CaptureError::operator(), this, _1)) - { - LLError::setPrintLocation(false); - } - - struct FatalException: public std::runtime_error - { - FatalException(const std::string& what): std::runtime_error(what) {} - }; - - void operator()(const std::string& message) - { - error = message; - throw FatalException(message); - } - - std::string error; - }; - template<> template<> void llcapears_object::test<1>() { @@ -160,10 +139,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -207,10 +186,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -269,10 +248,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } diff --git a/indra/newview/tests/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp index 30e39a3bcf..7ba82fbd2c 100644 --- a/indra/newview/tests/lldateutil_test.cpp +++ b/indra/newview/tests/lldateutil_test.cpp @@ -28,6 +28,9 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" + #include "../test/lltut.h" #include "../lldateutil.h" @@ -57,6 +60,11 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil:: std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count) { + count_string_t key(xml_desc, count); + if (gCountString.find(key) == gCountString.end()) + { + return std::string("Couldn't find ") + xml_desc; + } return gCountString[ count_string_t(xml_desc, count) ]; } @@ -88,8 +96,11 @@ namespace tut gCountString[ count_string_t("AgeYears", 2) ] = "2 years"; gCountString[ count_string_t("AgeMonths", 1) ] = "1 month"; gCountString[ count_string_t("AgeMonths", 2) ] = "2 months"; + gCountString[ count_string_t("AgeMonths", 11) ]= "11 months"; gCountString[ count_string_t("AgeWeeks", 1) ] = "1 week"; gCountString[ count_string_t("AgeWeeks", 2) ] = "2 weeks"; + gCountString[ count_string_t("AgeWeeks", 3) ] = "3 weeks"; + gCountString[ count_string_t("AgeWeeks", 4) ] = "4 weeks"; gCountString[ count_string_t("AgeDays", 1) ] = "1 day"; gCountString[ count_string_t("AgeDays", 2) ] = "2 days"; } @@ -110,12 +121,18 @@ namespace tut ensure_equals("years", LLDateUtil::ageFromDate("12/31/2007", mNow), "2 years old" ); - ensure_equals("single year", - LLDateUtil::ageFromDate("12/31/2008", mNow), - "1 year old" ); + ensure_equals("years", + LLDateUtil::ageFromDate("1/1/2008", mNow), + "1 year 11 months old" ); + ensure_equals("single year + one month", + LLDateUtil::ageFromDate("11/30/2008", mNow), + "1 year 1 month old" ); ensure_equals("single year + a bit", LLDateUtil::ageFromDate("12/12/2008", mNow), "1 year old" ); + ensure_equals("single year", + LLDateUtil::ageFromDate("12/31/2008", mNow), + "1 year old" ); } template<> template<> @@ -125,6 +142,9 @@ namespace tut ensure_equals("months", LLDateUtil::ageFromDate("10/30/2009", mNow), "2 months old" ); + ensure_equals("months 2", + LLDateUtil::ageFromDate("10/31/2009", mNow), + "2 months old" ); ensure_equals("single month", LLDateUtil::ageFromDate("11/30/2009", mNow), "1 month old" ); @@ -134,6 +154,9 @@ namespace tut void dateutil_object_t::test<3>() { set_test_name("Weeks"); + ensure_equals("4 weeks", + LLDateUtil::ageFromDate("12/1/2009", mNow), + "4 weeks old" ); ensure_equals("weeks", LLDateUtil::ageFromDate("12/17/2009", mNow), "2 weeks old" ); @@ -156,4 +179,14 @@ namespace tut LLDateUtil::ageFromDate("12/31/2009", mNow), "Joined today" ); } + + template<> template<> + void dateutil_object_t::test<5>() + { + set_test_name("2010 rollover"); + LLDate now(std::string("2010-01-04T12:00:00Z")); + ensure_equals("days", + LLDateUtil::ageFromDate("12/13/2009", now), + "3 weeks old" ); + } } diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp new file mode 100644 index 0000000000..ef93586c6e --- /dev/null +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -0,0 +1,424 @@ +/** + * @file lllogininstance_test.cpp + * @brief Test for lllogininstance.cpp. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * Copyright (c) 2008, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "../llviewerprecompiledheaders.h" +// Own header +#include "../lllogininstance.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "llevents.h" + +#if defined(LL_WINDOWS) +#pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr +#endif + +// Constants +const std::string VIEWERLOGIN_URI("viewerlogin_uri"); +const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid"); + +const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno"); + +// Link seams. + +//----------------------------------------------------------------------------- +static LLEventStream gTestPump("test_pump"); + +#include "lllogin.h" +static std::string gLoginURI; +static LLSD gLoginCreds; +static bool gDisconnectCalled = false; +class LLLogin::Impl +{ +}; +LLLogin::LLLogin() {} +LLLogin::~LLLogin() {} +LLEventPump& LLLogin::getEventPump() { return gTestPump; } +void LLLogin::connect(const std::string& uri, const LLSD& credentials) +{ + gLoginURI = uri; + gLoginCreds = credentials; +} + +void LLLogin::disconnect() +{ + gDisconnectCalled = true; +} + +//----------------------------------------------------------------------------- +#include "../llviewernetwork.h" +unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {'1','2','3','4','5','6'}; + +LLViewerLogin::LLViewerLogin() : mGridChoice(GRID_INFO_NONE) {} +LLViewerLogin::~LLViewerLogin() {} +void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const +{ + uris.push_back(VIEWERLOGIN_URI); +} +std::string LLViewerLogin::getGridLabel() const { return VIEWERLOGIN_GRIDLABEL; } + +//----------------------------------------------------------------------------- +#include "../llviewercontrol.h" +LLControlGroup gSavedSettings("Global"); +std::string gCurrentVersion = "invalid_version"; + +LLControlGroup::LLControlGroup(const std::string& name) : + LLInstanceTracker<LLControlGroup, std::string>(name){} +LLControlGroup::~LLControlGroup() {} +void LLControlGroup::setBOOL(const std::string& name, BOOL val) {} +BOOL LLControlGroup::getBOOL(const std::string& name) { return FALSE; } +F32 LLControlGroup::getF32(const std::string& name) { return 0.0f; } +U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only) { return 1; } +void LLControlGroup::setString(const std::string& name, const std::string& val) {} +std::string LLControlGroup::getString(const std::string& name) { return "test_string"; } +BOOL LLControlGroup::declareBOOL(const std::string& name, BOOL initial_val, const std::string& comment, BOOL persist) { return TRUE; } +BOOL LLControlGroup::declareString(const std::string& name, const std::string &initial_val, const std::string& comment, BOOL persist) { return TRUE; } + +#include "lluicolortable.h" +void LLUIColorTable::saveUserSettings(void)const {} + +//----------------------------------------------------------------------------- +#include "../llurlsimstring.h" +LLURLSimString LLURLSimString::sInstance; +bool LLURLSimString::parse() { return true; } + +//----------------------------------------------------------------------------- +#include "llnotifications.h" +#include "llfloaterreg.h" +static std::string gTOSType; +static LLEventPump * gTOSReplyPump = NULL; + +//static +LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, BOOL focus) +{ + gTOSType = name; + gTOSReplyPump = &LLEventPumps::instance().obtain(key["reply_pump"]); + return NULL; +} + +//----------------------------------------------------------------------------- +// LLNotifications +class MockNotifications : public LLNotificationsInterface +{ + boost::function<void (const LLSD&, const LLSD&)> mResponder; + int mAddedCount; + +public: + MockNotifications() : + mResponder(0), + mAddedCount(0) + { + } + + virtual ~MockNotifications() {} + + /* virtual */ LLNotificationPtr add( + const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor) + { + mResponder = functor; + mAddedCount++; + return LLNotificationPtr((LLNotification*)NULL); + } + + void sendYesResponse() + { + LLSD notification; + LLSD response; + response = 1; + mResponder(notification, response); + } + + void sendNoResponse() + { + LLSD notification; + LLSD response; + response = 2; + mResponder(notification, response); + } + + void sendBogusResponse() + { + LLSD notification; + LLSD response; + response = 666; + mResponder(notification, response); + } + + int addedCount() { return mAddedCount; } +}; + +S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) +{ + return response.asInteger(); +} + +// misc +std::string xml_escape_string(const std::string& in) +{ + return in; +} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct lllogininstance_data + { + lllogininstance_data() : logininstance(LLLoginInstance::getInstance()) + { + // Global initialization + gLoginURI.clear(); + gLoginCreds.clear(); + gDisconnectCalled = false; + + gTOSType = ""; // Set to invalid value. + gTOSReplyPump = 0; // clear the callback. + + + gSavedSettings.declareBOOL("NoInventoryLibrary", FALSE, "", FALSE); + gSavedSettings.declareBOOL("ConnectAsGod", FALSE, "", FALSE); + gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", FALSE); + gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", FALSE); + gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", FALSE); + gSavedSettings.declareString("VersionChannelName", "test_version_string", "", FALSE); + gSavedSettings.declareString("NextLoginLocation", "", "", FALSE); + gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE); + + credentials["first"] = "testfirst"; + credentials["last"] = "testlast"; + credentials["passwd"] = "testpass"; + + logininstance->setNotificationsInterface(¬ifications); + } + + LLLoginInstance* logininstance; + LLSD credentials; + MockNotifications notifications; + }; + + typedef test_group<lllogininstance_data> lllogininstance_group; + typedef lllogininstance_group::object lllogininstance_object; + lllogininstance_group llsdmgr("lllogininstance"); + + template<> template<> + void lllogininstance_object::test<1>() + { + set_test_name("Test Simple Success And Disconnect"); + + // Test default connect. + logininstance->connect(credentials); + + ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); + + // Dummy success response. + LLSD response; + response["state"] = "online"; + response["change"] = "connect"; + response["progress"] = 1.0; + response["transfer_rate"] = 7; + response["data"] = "test_data"; + + gTestPump.post(response); + + ensure("Success response", logininstance->authSuccess()); + ensure_equals("Test Response Data", logininstance->getResponse().asString(), "test_data"); + + logininstance->disconnect(); + + ensure_equals("Called Login Module Disconnect", gDisconnectCalled, true); + + response.clear(); + response["state"] = "offline"; + response["change"] = "disconnect"; + response["progress"] = 0.0; + response["transfer_rate"] = 0; + response["data"] = "test_data"; + + gTestPump.post(response); + + ensure("Disconnected", !(logininstance->authSuccess())); + } + + template<> template<> + void lllogininstance_object::test<2>() + { + set_test_name("Test User TOS/Critical message Interaction"); + + const std::string test_uri = "testing-uri"; + + // Test default connect. + logininstance->connect(test_uri, credentials); + + // connect should call LLLogin::connect to init gLoginURI and gLoginCreds. + ensure_equals("Default connect uri", gLoginURI, "testing-uri"); + ensure_equals("Default for agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), false); + ensure_equals("Default for read critical", gLoginCreds["params"]["read_critical"].asBoolean(), false); + + // TOS failure response. + LLSD response; + response["state"] = "offline"; + response["change"] = "fail.login"; + response["progress"] = 0.0; + response["transfer_rate"] = 7; + response["data"]["reason"] = "tos"; + gTestPump.post(response); + + ensure_equals("TOS Dialog type", gTOSType, "message_tos"); + ensure("TOS callback given", gTOSReplyPump != 0); + gTOSReplyPump->post(false); // Call callback denying TOS. + ensure("No TOS, failed auth", logininstance->authFailure()); + + // Start again. + logininstance->connect(test_uri, credentials); + gTestPump.post(response); // Fail for tos again. + gTOSReplyPump->post(true); // Accept tos, should reconnect w/ agree_to_tos. + ensure_equals("Accepted agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), true); + ensure("Incomplete login status", !logininstance->authFailure() && !logininstance->authSuccess()); + + // Fail connection, attempt connect again. + // The new request should have reset agree to tos to default. + response["data"]["reason"] = "key"; // bad creds. + gTestPump.post(response); + ensure("TOS auth failure", logininstance->authFailure()); + + logininstance->connect(test_uri, credentials); + ensure_equals("Reset to default for agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), false); + + // Critical Message failure response. + logininstance->connect(test_uri, credentials); + response["data"]["reason"] = "critical"; // Change response to "critical message" + gTestPump.post(response); + + ensure_equals("TOS Dialog type", gTOSType, "message_critical"); + ensure("TOS callback given", gTOSReplyPump != 0); + gTOSReplyPump->post(true); + ensure_equals("Accepted read critical message", gLoginCreds["params"]["read_critical"].asBoolean(), true); + ensure("Incomplete login status", !logininstance->authFailure() && !logininstance->authSuccess()); + + // Fail then attempt new connection + response["data"]["reason"] = "key"; // bad creds. + gTestPump.post(response); + ensure("TOS auth failure", logininstance->authFailure()); + logininstance->connect(test_uri, credentials); + ensure_equals("Default for agree to tos", gLoginCreds["params"]["read_critical"].asBoolean(), false); + } + + template<> template<> + void lllogininstance_object::test<3>() + { + set_test_name("Test Mandatory Update User Accepts"); + + // Part 1 - Mandatory Update, with User accepts response. + // Test connect with update needed. + logininstance->connect(credentials); + + ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); + + // Update needed failure response. + LLSD response; + response["state"] = "offline"; + response["change"] = "fail.login"; + response["progress"] = 0.0; + response["transfer_rate"] = 7; + response["data"]["reason"] = "update"; + gTestPump.post(response); + + ensure_equals("Notification added", notifications.addedCount(), 1); + + notifications.sendYesResponse(); + + ensure("Disconnected", !(logininstance->authSuccess())); + } + + template<> template<> + void lllogininstance_object::test<4>() + { + set_test_name("Test Mandatory Update User Decline"); + + // Test connect with update needed. + logininstance->connect(credentials); + + ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); + + // Update needed failure response. + LLSD response; + response["state"] = "offline"; + response["change"] = "fail.login"; + response["progress"] = 0.0; + response["transfer_rate"] = 7; + response["data"]["reason"] = "update"; + gTestPump.post(response); + + ensure_equals("Notification added", notifications.addedCount(), 1); + notifications.sendNoResponse(); + + ensure("Disconnected", !(logininstance->authSuccess())); + } + + template<> template<> + void lllogininstance_object::test<6>() + { + set_test_name("Test Optional Update User Accept"); + + // Part 3 - Mandatory Update, with bogus response. + // Test connect with update needed. + logininstance->connect(credentials); + + ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); + + // Update needed failure response. + LLSD response; + response["state"] = "offline"; + response["change"] = "fail.login"; + response["progress"] = 0.0; + response["transfer_rate"] = 7; + response["data"]["reason"] = "optional"; + gTestPump.post(response); + + ensure_equals("Notification added", notifications.addedCount(), 1); + notifications.sendYesResponse(); + + ensure("Disconnected", !(logininstance->authSuccess())); + } + + template<> template<> + void lllogininstance_object::test<7>() + { + set_test_name("Test Optional Update User Denies"); + + // Part 3 - Mandatory Update, with bogus response. + // Test connect with update needed. + logininstance->connect(credentials); + + ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); + + // Update needed failure response. + LLSD response; + response["state"] = "offline"; + response["change"] = "fail.login"; + response["progress"] = 0.0; + response["transfer_rate"] = 7; + response["data"]["reason"] = "optional"; + gTestPump.post(response); + + ensure_equals("Notification added", notifications.addedCount(), 1); + notifications.sendNoResponse(); + + // User skips, should be reconnecting. + ensure_equals("reconnect uri", gLoginURI, VIEWERLOGIN_URI); + ensure_equals("skipping optional update", gLoginCreds["params"]["skipoptional"].asBoolean(), true); + } +} diff --git a/indra/newview/tests/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp new file mode 100644 index 0000000000..33d413bd21 --- /dev/null +++ b/indra/newview/tests/llmediadataclient_test.cpp @@ -0,0 +1,938 @@ +/** + * @file llmediadataclient_test.cpp + * @brief LLMediaDatClient tests + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, 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 "../llviewerprecompiledheaders.h" + +#include <iostream> +#include "../test/lltut.h" + +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llerrorcontrol.h" +#include "llhttpstatuscodes.h" + +#include "../llmediadataclient.h" +#include "../llvovolume.h" + +#include "../../llprimitive/llmediaentry.cpp" +#include "../../llprimitive/lltextureentry.cpp" +#include "../../llmessage/tests/llcurl_stub.cpp" + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable : 4702) // boost::lexical_cast generates this warning +#endif +#include <boost/lexical_cast.hpp> +#if LL_WINDOWS +#pragma warning (pop) +#endif + +#define VALID_OBJECT_ID "3607d5c4-644b-4a8a-871a-8b78471af2a2" +#define VALID_OBJECT_ID_1 "11111111-1111-1111-1111-111111111111" +#define VALID_OBJECT_ID_2 "22222222-2222-2222-2222-222222222222" +#define VALID_OBJECT_ID_3 "33333333-3333-3333-3333-333333333333" +#define VALID_OBJECT_ID_4 "44444444-4444-4444-4444-444444444444" + +#define FAKE_OBJECT_MEDIA_CAP_URL "foo_ObjectMedia" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL "foo_ObjectMediaNavigate" +#define FAKE_OBJECT_MEDIA_CAP_URL_503 "foo_ObjectMedia_503" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR "foo_ObjectMediaNavigate_ERROR" + +#define MEDIA_DATA "\ +<array> \ +<string>foo</string> \ +<string>bar</string> \ +<string>baz</string> \ +</array>" + +#define _DATA_URLS(ID,INTEREST,NEW,URL1,URL2) " \ +<llsd> \ + <map> \ + <key>uuid</key> \ + <string>" ID "</string> \ + <key>interest</key> \ + <real>" INTEREST "</real> \ + <key>cap_urls</key> \ + <map> \ + <key>ObjectMedia</key> \ + <string>" URL1 "</string> \ + <key>ObjectMediaNavigate</key> \ + <string>" URL2 "</string> \ + </map> \ + <key>media_data</key> \ + " MEDIA_DATA " \ + <key>is_dead</key> \ + <boolean>false</boolean> \ + <key>is_new</key> \ + <boolean>" NEW "</boolean> \ + </map> \ +</llsd>" + +#define _DATA(ID,INTEREST,NEW) _DATA_URLS(ID,INTEREST,NEW,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL) + +const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","true"); + +#define STR(I) boost::lexical_cast<std::string>(I) + +#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" << \ +"================================================================================\n" << \ +"==================================== TEST " #N " ===================================\n" << \ +"================================================================================\n" << LL_ENDL; + +LLSD *gPostRecords = NULL; +F64 gMinimumInterestLevel = (F64)0.0; + +// stubs: +void LLHTTPClient::post( + const std::string& url, + const LLSD& body, + LLHTTPClient::ResponderPtr responder, + const LLSD& headers, + const F32 timeout) +{ + LLSD record; + record["url"] = url; + record["body"] = body; + record["headers"] = headers; + record["timeout"] = timeout; + gPostRecords->append(record); + + // Magic URL that triggers a 503: + LLSD result; + result[LLTextureEntry::OBJECT_ID_KEY] = body[LLTextureEntry::OBJECT_ID_KEY]; + if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 ) + { + responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason"); + return; + } + else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR) + { + LLSD error; + error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE; + result["error"] = error; + } + responder->result(result); +} + +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + +class LLMediaDataClientObjectTest : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectTest(const char *data) + { + std::istringstream d(data); + LLSDSerialize::fromXML(mRep, d); + mNumBounceBacks = 0; + + // std::cout << ll_pretty_print_sd(mRep) << std::endl; + // std::cout << "ID: " << getID() << std::endl; + } + LLMediaDataClientObjectTest(const LLSD &rep) + : mRep(rep), mNumBounceBacks(0) {} + ~LLMediaDataClientObjectTest() + { LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; } + + virtual U8 getMediaDataCount() const + { return mRep["media_data"].size(); } + virtual LLSD getMediaDataLLSD(U8 index) const + { return mRep["media_data"][(LLSD::Integer)index]; } + virtual LLUUID getID() const + { return mRep["uuid"]; } + virtual void mediaNavigateBounceBack(U8 index) + { mNumBounceBacks++; } + + virtual bool hasMedia() const + { return mRep.has("media_data"); } + + virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &media_version) + { mRep["media_data"] = media_data_array; mRep["media_version"] = media_version; } + + virtual F64 getMediaInterest() const + { return (LLSD::Real)mRep["interest"]; } + + virtual bool isInterestingEnough() const + { return getMediaInterest() > gMinimumInterestLevel; } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mRep["cap_urls"][name]; } + + virtual bool isDead() const + { return mRep["is_dead"]; } + + virtual U32 getMediaVersion() const + { return (LLSD::Integer)mRep["media_version"]; } + + virtual bool isNew() const + { return mRep["is_new"]; } + + void setMediaInterest(F64 val) + { mRep["interest"] = val; } + + int getNumBounceBacks() const + { return mNumBounceBacks; } + + void markDead() + { mRep["is_dead"] = true; } + + void markOld() + { mRep["is_new"] = false; } + +private: + LLSD mRep; + int mNumBounceBacks; +}; + +// This special timer delay should ensure that the timer will fire on the very +// next pump, no matter what (though this does make an assumption about the +// implementation of LLEventTimer::updateClass()): +const F32 NO_PERIOD = -1000.0f; + +static void pump_timers() +{ + LLEventTimer::updateClass(); +} + +namespace tut +{ + struct mediadataclient + { + mediadataclient() { + gPostRecords = &mLLSD; + gMinimumInterestLevel = (F64)0.0; + +// LLError::setDefaultLevel(LLError::LEVEL_DEBUG); +// LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG); +// LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG); + } + LLSD mLLSD; + }; + + typedef test_group<mediadataclient> mediadataclient_t; + typedef mediadataclient_t::object mediadataclient_object_t; + tut::mediadataclient_t tut_mediadataclient("mediadataclient"); + + void ensure(const std::string &msg, int value, int expected) + { + std::string m = msg; + m += " value: " + STR(value); + m += ", expected: " + STR(expected); + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const std::string & value, const std::string & expected) + { + std::string m = msg; + m += " value: " + value; + m += ", expected: " + expected; + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const LLUUID & value, const LLUUID & expected) + { + std::string m = msg; + m += " value: " + value.asString(); + m += ", expected: " + expected.asString(); + ensure(m, value == expected); + } + + void ensure_llsd(const std::string &msg, const LLSD & value, const char *expected) + { + LLSD expected_llsd; + std::istringstream e(expected); + LLSDSerialize::fromXML(expected_llsd, e); + + std::string value_str = ll_pretty_print_sd(value); + std::string expected_str = ll_pretty_print_sd(expected_llsd); + std::string m = msg; + m += " value: " + value_str; + m += ", expected: " + expected_str; + ensure(m, value_str == expected_str); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<1>() + { + // + // Test fetchMedia() + // + LOG_TEST(1); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + int num_refs_start = o->getNumRefs(); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + ::pump_timers(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post GET", (*gPostRecords)[0]["body"]["verb"], "GET"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<2>() + { + // + // Test updateMedia() + // + LOG_TEST(2); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + // queue time w/ no delay ensures that ::pump_timers() will hit the tick() + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + mdc->updateMedia(o); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post UPDATE", (*gPostRecords)[0]["body"]["verb"], "UPDATE"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure_llsd("post data llsd", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + "<llsd>" MEDIA_DATA "</llsd>"); + ensure("queue empty", mdc->isEmpty()); + } + + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<3>() + { + // + // Test navigate() + // + LOG_TEST(3); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + // ensure no bounce back + ensure("bounce back", dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), 0); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<4>() + { + // + // Test queue ordering + // + LOG_TEST(4); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_1,"1.0","true")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_2,"3.0","true")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_3,"2.0","true")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 }; + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + // tick 3 times... + ::pump_timers(); + ensure("post records", gPostRecords->size(), 1); + ::pump_timers(); + ensure("post records", gPostRecords->size(), 2); + ::pump_timers(); + ensure("post records", gPostRecords->size(), 3); + + for( int i=0; i < 3; i++ ) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), + LLUUID(ORDERED_OBJECT_IDS[i])); + } + + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<5>() + { + // + // Test fetchMedia() getting a 503 error + // + LOG_TEST(5); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0","true", + FAKE_OBJECT_MEDIA_CAP_URL_503, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)); + int num_refs_start = o->getNumRefs(); + { + const int NUM_RETRIES = 5; + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD,NUM_RETRIES); + + // This should generate a retry + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records before", gPostRecords->size(), 0); + + // Once, causes retry + // Second, fires retry timer + // Third, fires queue timer again + for (int i=0; i<NUM_RETRIES; ++i) + { + ::pump_timers(); // Should pump (fire) the queue timer, causing a retry timer to be scheduled + // XXX This ensure is not guaranteed, because scheduling a timer might actually get it pumped in the same loop + //ensure("post records " + STR(i), gPostRecords->size(), i+1); + ::pump_timers(); // Should pump (fire) the retry timer, scheduling the queue timer + } + + // Do some extra pumps to make sure no other timer work occurs. + ::pump_timers(); + ::pump_timers(); + ::pump_timers(); + + // Make sure there were 2 posts + ensure("post records after", gPostRecords->size(), NUM_RETRIES); + for (int i=0; i<NUM_RETRIES; ++i) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL_503); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + } + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + template<> template<> + void mediadataclient_object_t::test<6>() + { + // + // Test navigate() with a bounce back + // + LOG_TEST(6); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0","true", + FAKE_OBJECT_MEDIA_CAP_URL, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + // ensure bounce back + ensure("bounce back", + dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), + 1); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + template<> template<> + void mediadataclient_object_t::test<7>() + { + // Test LLMediaDataClient::isInQueue() + LOG_TEST(7); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_1,"3.0","true")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_2,"1.0","true")); + int num_refs_start = o1->getNumRefs(); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + + ensure("not in queue yet 1", ! mdc->isInQueue(o1)); + ensure("not in queue yet 2", ! mdc->isInQueue(o2)); + + mdc->fetchMedia(o1); + + ensure("is in queue", mdc->isInQueue(o1)); + ensure("is not in queue", ! mdc->isInQueue(o2)); + + ::pump_timers(); + + ensure("not in queue anymore", ! mdc->isInQueue(o1)); + ensure("still is not in queue", ! mdc->isInQueue(o2)); + + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o1->getNumRefs(), num_refs_start); + + } + + template<> template<> + void mediadataclient_object_t::test<8>() + { + // Test queue handling of objects that are marked dead. + LOG_TEST(8); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"4.0","true")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"3.0","true")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"2.0","true")); + LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"1.0","true")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + + // queue up all 4 objects + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + mdc->fetchMedia(o4); + + // and mark the second and fourth ones dead. + dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o2))->markDead(); + dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o4))->markDead(); + + ensure("is in queue 1", mdc->isInQueue(o1)); + ensure("is in queue 2", mdc->isInQueue(o2)); + ensure("is in queue 3", mdc->isInQueue(o3)); + ensure("is in queue 4", mdc->isInQueue(o4)); + ensure("post records", gPostRecords->size(), 0); + + ::pump_timers(); + + // The first tick should remove the first one + ensure("is not in queue 1", !mdc->isInQueue(o1)); + ensure("is in queue 2", mdc->isInQueue(o2)); + ensure("is in queue 3", mdc->isInQueue(o3)); + ensure("is in queue 4", mdc->isInQueue(o4)); + ensure("post records", gPostRecords->size(), 1); + + ::pump_timers(); + + // The second tick should skip the second and remove the third + ensure("is not in queue 2", !mdc->isInQueue(o2)); + ensure("is not in queue 3", !mdc->isInQueue(o3)); + ensure("is in queue 4", mdc->isInQueue(o4)); + ensure("post records", gPostRecords->size(), 2); + + ::pump_timers(); + + // The third tick should skip the fourth one and empty the queue. + ensure("is not in queue 4", !mdc->isInQueue(o4)); + ensure("post records", gPostRecords->size(), 2); + + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + ensure("refcount of o4", o4->getNumRefs(), 1); + + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<9>() + { + // + // Test queue re-ordering + // + LOG_TEST(9); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"40.0","true")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"30.0","true")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"20.0","true")); + LLMediaDataClientObjectTest *object4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"10.0","true")); + LLMediaDataClientObject::ptr_t o4 = object4; + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + + // queue up all 4 objects. They should now be in the queue in + // order 1 through 4, with 4 being at the front of the queue + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + mdc->fetchMedia(o4); + + int tick_num = 0; + + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0); + + ::pump_timers(); + ++tick_num; + + // The first tick should remove the first one + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1); + + // Now, pretend that object 4 moved relative to the avatar such + // that it is now closest + object4->setMediaInterest(50.0); + + ::pump_timers(); + ++tick_num; + + // The second tick should still pick off item 2, but then re-sort + // have picked off object 4 + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2); + + ::pump_timers(); + ++tick_num; + + // The third tick should pick off object 2 + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3); + + // The fourth tick should pick off object 3 + ::pump_timers(); + ++tick_num; + + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4); + + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + ensure("refcount of o4", o4->getNumRefs(), 1); + } + + + template<> template<> + void mediadataclient_object_t::test<10>() + { + // + // Test using the "round-robin" queue + // + LOG_TEST(10); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","false")); + LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","false")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + + // queue up all 4 objects. The first two should be in the sorted + // queue [2 1], the second in the round-robin queue. The queues + // are serviced interleaved, so we should expect: + // 2, 4, 1, 3 + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + mdc->fetchMedia(o4); + + int tick_num = 0; + + // 0 + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0); + + ::pump_timers(); + ++tick_num; + + // 1 The first tick should remove object 2 + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_2)); + + ::pump_timers(); + ++tick_num; + + // 2 The second tick should send object 4, but it will still be + // "in the queue" + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4)); + + ::pump_timers(); + ++tick_num; + + // 3 The third tick should remove object 1 + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1)); + + ::pump_timers(); + ++tick_num; + + // 4 The fourth tick should send object 3, but it will still be + // "in the queue" + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[3]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3)); + + ::pump_timers(); + ++tick_num; + + // 5 The fifth tick should now identify objects 3 and 4 as no longer + // needing "updating", and remove them from the queue + ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4); + + ::pump_timers(); + + // Whew....better be empty + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + ensure("refcount of o4", o4->getNumRefs(), 1); + } + + + template<> template<> + void mediadataclient_object_t::test<11>() + { + // + // Test LLMediaDataClient's destructor + // + LOG_TEST(11); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + int num_refs_start = o->getNumRefs(); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + mdc->fetchMedia(o); + // must tick enough times to clear refcount of mdc + ::pump_timers(); + } + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + template<> template<> + void mediadataclient_object_t::test<12>() + { + // + // Test the "not interesting enough" call + // + LOG_TEST(12); + + LLMediaDataClientObjectTest *object1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true")); + LLMediaDataClientObject::ptr_t o1 = object1; + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","true")); + LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","true")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + + // queue up all 4 objects. The first two are "interesting enough". + // Firing the timer 4 times should therefore leave them. + // Note that they should be sorted 4,3,2,1 + // Then, we'll make one "interesting enough", fire the timer a few + // times, and make sure only it gets pulled off the queue + gMinimumInterestLevel = 2.5; + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + mdc->fetchMedia(o4); + + int tick_num = 0; + + // 0 + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 0); + + ::pump_timers(); + ++tick_num; + + // 1 The first tick should remove object 4 + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4)); + + ::pump_timers(); + ++tick_num; + + // 2 The second tick should send object 3 + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3)); + + ::pump_timers(); + ++tick_num; + + // 3 The third tick should not pull off anything + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2); + + ::pump_timers(); + ++tick_num; + + // 4 The fourth tick (for good measure) should not pull off anything + ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2); + + // Okay, now futz with object 1's interest, such that it is now + // "interesting enough" + object1->setMediaInterest((F64)5.0); + + // This should sort so that the queue is now [1 2] + ::pump_timers(); + ++tick_num; + + // 5 The fifth tick should now identify objects 3 and 4 as no longer + // needing "updating", and remove them from the queue + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3); + ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1)); + + ::pump_timers(); + ++tick_num; + + // 6 The sixth tick should not pull off anything + ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); + ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2)); + ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3)); + ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4)); + ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3); + + ::pump_timers(); + ++tick_num; + + // Whew....better NOT be empty ... o2 should still be there + ensure("queue not empty", !mdc->isEmpty()); + + // But, we need to clear the queue, or else we won't destroy MDC... + // this is a strange interplay between the queue timer and the MDC + ensure("o2 couldn't be removed from queue", mdc->removeFromQueue(o2)); + // tick + ::pump_timers(); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + ensure("refcount of o4", o4->getNumRefs(), 1); + } +} diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp new file mode 100644 index 0000000000..8dfba46262 --- /dev/null +++ b/indra/newview/tests/lltextureinfo_test.cpp @@ -0,0 +1,284 @@ +/** + * @file llwtextureinfo_test.cpp + * @author Si & Gabriel + * @date 2009-03-30 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, 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$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltextureinfo.h" +// Dependencies +#include "../lltextureinfodetails.cpp" + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct textureinfo_test + { + // Constructor and destructor of the test wrapper + textureinfo_test() + { + } + ~textureinfo_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<textureinfo_test> textureinfo_t; + typedef textureinfo_t::object textureinfo_object_t; + tut::textureinfo_t tut_textureinfo("textureinfo"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void textureinfo_object_t::test<1>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + ensure("have we crashed?", true); + } + + // Check lltextureinfo does not contain UUIDs we haven't added + template<> template<> + void textureinfo_object_t::test<2>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure(!tex_info.has(nonExistant)); + } + + // Check we can add a request time for a texture + template<> template<> + void textureinfo_object_t::test<3>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id, 200); + + ensure_equals(tex_info.getRequestStartTime(id), 200); + } + + // Check time for non-existant texture + template<> template<> + void textureinfo_object_t::test<4>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure_equals(tex_info.getRequestStartTime(nonExistant), 0); + } + + // Check download complete time for non existant texture + template<> template<> + void textureinfo_object_t::test<5>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure_equals(tex_info.getRequestCompleteTime(nonExistant), 0); + } + + // requested size is passed in correctly + template<> template<> + void textureinfo_object_t::test<6>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestSize(id, 600); + + ensure_equals(tex_info.getRequestSize(id), 600); + } + + // transport type is recorded correctly (http) + template<> template<> + void textureinfo_object_t::test<7>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + + ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_HTTP); + } + + // transport type is recorded correctly (udp) + template<> template<> + void textureinfo_object_t::test<8>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_UDP); + + ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_UDP); + } + + // request offset is recorded correctly + template<> template<> + void textureinfo_object_t::test<9>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestOffset(id, 1234); + + ensure_equals(tex_info.getRequestOffset(id), 1234); + } + + // ask for averages gives us correct figure + template<> template<> + void textureinfo_object_t::test<10>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + S32 requestSizeOneBits = requestSizeOne * 8; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + U32 requestStartTimeTwo = 100; + U32 requestEndTimeTwo = 500; + U32 requestSizeTwo = 2048; + S32 requestSizeTwoBits = requestSizeTwo * 8; + LLUUID id2("10e65d70-46fd-429f-841a-bf698e9424d4"); + tex_info.setRequestStartTime(id2, requestStartTimeTwo); + tex_info.setRequestSize(id2, requestSizeTwo); + tex_info.setRequestType(id2, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id2, requestEndTimeTwo); + + S32 averageBitRate = ((requestSizeOneBits/(requestEndTimeOne - requestStartTimeOne)) + + (requestSizeTwoBits/(requestEndTimeTwo - requestStartTimeTwo))) / 2; + + S32 totalBytes = requestSizeOne + requestSizeTwo; + + LLSD results = tex_info.getAverages(); + ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), averageBitRate); + ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), totalBytes); + ensure_equals("is transport correct", results["transport"].asString(), std::string("HTTP")); + } + + // make sure averages cleared when reset is called + template<> template<> + void textureinfo_object_t::test<11>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + tex_info.getAverages(); + tex_info.reset(); + LLSD results = tex_info.getAverages(); + ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), 0); + ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), 0); + ensure_equals("is transport correct", results["transport"].asString(), std::string("NONE")); + } + + // make sure map item removed when expired + template<> template<> + void textureinfo_object_t::test<12>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + + ensure_equals("map item created", tex_info.getTextureInfoMapSize(), 1); + + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + ensure_equals("map item removed when consumed", tex_info.getTextureInfoMapSize(), 0); + } +} + diff --git a/indra/newview/tests/lltextureinfodetails_test.cpp b/indra/newview/tests/lltextureinfodetails_test.cpp new file mode 100644 index 0000000000..aa2697fb8e --- /dev/null +++ b/indra/newview/tests/lltextureinfodetails_test.cpp @@ -0,0 +1,98 @@ +/** + * @file llwtextureinfodetails_test.cpp + * @author Si & Gabriel + * @date 2009-03-30 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, 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$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltextureinfodetails.h" +// Dependencies + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct textureinfodetails_test + { + // Constructor and destructor of the test wrapper + textureinfodetails_test() + { + } + ~textureinfodetails_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<textureinfodetails_test> textureinfodetails_t; + typedef textureinfodetails_t::object textureinfodetails_object_t; + tut::textureinfodetails_t tut_textureinfodetails("textureinfodetails"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void textureinfodetails_object_t::test<1>() + { + ensure("have we crashed?", true); + } +} diff --git a/indra/newview/tests/lltexturestatsuploader_test.cpp b/indra/newview/tests/lltexturestatsuploader_test.cpp new file mode 100644 index 0000000000..77a3e2c3d8 --- /dev/null +++ b/indra/newview/tests/lltexturestatsuploader_test.cpp @@ -0,0 +1,156 @@ +/** + * @file lltexturestatsuploader_test.cpp + * @author Si + * @date 2009-05-27 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, 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$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltexturestatsuploader.h" +// Dependencies + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +#include "boost/intrusive_ptr.hpp" +void boost::intrusive_ptr_add_ref(LLCurl::Responder*){} +void boost::intrusive_ptr_release(LLCurl::Responder* p){} +const F32 HTTP_REQUEST_EXPIRY_SECS = 0.0f; + +static std::string most_recent_url; +static LLSD most_recent_body; + +void LLHTTPClient::post( + const std::string& url, + const LLSD& body, + ResponderPtr, + const LLSD& headers, + const F32 timeout) +{ + // set some sensor code + most_recent_url = url; + most_recent_body = body; + return; +} + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct texturestatsuploader_test + { + // Constructor and destructor of the test wrapper + texturestatsuploader_test() + { + most_recent_url = "some sort of default text that should never match anything the tests are expecting!"; + LLSD blank_llsd; + most_recent_body = blank_llsd; + } + ~texturestatsuploader_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<texturestatsuploader_test> texturestatsuploader_t; + typedef texturestatsuploader_t::object texturestatsuploader_object_t; + tut::texturestatsuploader_t tut_texturestatsuploader("texturestatsuploader"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void texturestatsuploader_object_t::test<1>() + { + LLTextureStatsUploader tsu; + llinfos << &tsu << llendl; + ensure("have we crashed?", true); + } + + // does it call out to the provided url if we ask it to? + template<> template<> + void texturestatsuploader_object_t::test<2>() + { + LLTextureStatsUploader tsu; + std::string url = "http://blahblahblah"; + LLSD texture_stats; + tsu.uploadStatsToSimulator(url, texture_stats); + ensure_equals("did the right url get called?", most_recent_url, url); + ensure_equals("did the right body get sent?", most_recent_body, texture_stats); + } + + // does it not call out to the provided url if we send it an ungranted cap? + template<> template<> + void texturestatsuploader_object_t::test<3>() + { + LLTextureStatsUploader tsu; + + // this url left intentionally blank to mirror + // not getting a cap in the caller. + std::string url_for_ungranted_cap = ""; + + LLSD texture_stats; + std::string most_recent_url_before_test = most_recent_url; + tsu.uploadStatsToSimulator(url_for_ungranted_cap, texture_stats); + + ensure_equals("hopefully no url got called!", most_recent_url, most_recent_url_before_test); + } + + // does it call out if the data is empty? + // should it even do that? +} + diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 40f7d532bc..eecc79d36c 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -30,48 +30,81 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ +// Precompiled header +#include "../llviewerprecompiledheaders.h" + #include "../test/lltut.h" #include "../llviewerhelputil.h" +#include "../llweb.h" #include "llcontrol.h" -#include "llsys.h" #include <iostream> -//---------------------------------------------------------------------------- -// Implementation of enough of LLControlGroup to support the tests: +// values for all of the supported substitutions parameters +static std::string gHelpURL; +static std::string gVersion; +static std::string gChannel; +static std::string gLanguage; +static std::string gGrid; +static std::string gOS; -static std::map<std::string,std::string> test_stringvec; +//---------------------------------------------------------------------------- +// Mock objects for the dependencies of the code we're testing LLControlGroup::LLControlGroup(const std::string& name) - : LLInstanceTracker<LLControlGroup, std::string>(name) -{ -} - -LLControlGroup::~LLControlGroup() -{ -} - -// Implementation of just the LLControlGroup methods we requre + : LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} BOOL LLControlGroup::declareString(const std::string& name, const std::string& initial_val, const std::string& comment, - BOOL persist) + BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) { - test_stringvec[name] = initial_val; - return true; + if (name == "HelpURLFormat") + return gHelpURL; + return ""; } +LLControlGroup gSavedSettings("test"); -void LLControlGroup::setString(const std::string& name, const std::string& val) +static void substitute_string(std::string &input, const std::string &search, const std::string &replace) { - test_stringvec[name] = val; + size_t pos = input.find(search); + while (pos != std::string::npos) + { + input = input.replace(pos, search.size(), replace); + pos = input.find(search); + } } -std::string LLControlGroup::getString(const std::string& name) +class LLAgent { - return test_stringvec[name]; +public: + LLAgent() {} + ~LLAgent() {} +#ifdef __GNUC__ + __attribute__ ((noinline)) +#endif + bool isGodlike() const { return FALSE; } +}; +LLAgent gAgent; + +std::string LLWeb::expandURLSubstitutions(const std::string &url, + const LLSD &default_subs) +{ + (void)gAgent.isGodlike(); // ref symbol to stop compiler from stripping it + std::string new_url = url; + substitute_string(new_url, "[TOPIC]", default_subs["TOPIC"].asString()); + substitute_string(new_url, "[VERSION]", gVersion); + substitute_string(new_url, "[CHANNEL]", gChannel); + substitute_string(new_url, "[LANGUAGE]", gLanguage); + substitute_string(new_url, "[GRID]", gGrid); + substitute_string(new_url, "[OS]", gOS); + return new_url; } + //---------------------------------------------------------------------------- namespace tut @@ -87,41 +120,52 @@ namespace tut template<> template<> void viewerhelputil_object_t::test<1>() { - LLOSInfo osinfo; - LLControlGroup cgr("test"); - cgr.declareString("HelpURLFormat", "fooformat", "declared_for_test", FALSE); - cgr.declareString("VersionChannelName", "foochannelname", "declared_for_test", FALSE); - cgr.declareString("Language", "foolanguage", "declared_for_test", FALSE); std::string topic("test_topic"); - std::string subresult; - cgr.setString("HelpURLFormat", "fooformat"); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = "fooformat"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("no substitution tags", subresult, "fooformat"); - cgr.setString("HelpURLFormat", ""); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = ""; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("blank substitution format", subresult, ""); - cgr.setString("HelpURLFormat", "[LANGUAGE]"); - cgr.setString("Language", ""); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = "[TOPIC]"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); + ensure_equals("topic name", subresult, "test_topic"); + + gHelpURL = "[LANGUAGE]"; + gLanguage = ""; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("simple substitution with blank", subresult, ""); - cgr.setString("HelpURLFormat", "[LANGUAGE]"); - cgr.setString("Language", "Esperanto"); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = "[LANGUAGE]"; + gLanguage = "Esperanto"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("simple substitution", subresult, "Esperanto"); - cgr.setString("HelpURLFormat", "[XXX]"); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = "http://secondlife.com/[LANGUAGE]"; + gLanguage = "Gaelic"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); + ensure_equals("simple substitution with url", subresult, "http://secondlife.com/Gaelic"); + + gHelpURL = "[XXX]"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("unknown substitution", subresult, "[XXX]"); - cgr.setString("HelpURLFormat", "[LANGUAGE]/[LANGUAGE]"); - cgr.setString("Language", "Esperanto"); - subresult = LLViewerHelpUtil::buildHelpURL(topic, cgr, osinfo); + gHelpURL = "[LANGUAGE]/[LANGUAGE]"; + gLanguage = "Esperanto"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); ensure_equals("multiple substitution", subresult, "Esperanto/Esperanto"); + + gHelpURL = "http://[CHANNEL]/[VERSION]/[LANGUAGE]/[OS]/[GRID]/[XXX]"; + gChannel = "Second Life Test"; + gVersion = "2.0"; + gLanguage = "gaelic"; + gOS = "AmigaOS 2.1"; + gGrid = "mysim"; + subresult = LLViewerHelpUtil::buildHelpURL(topic); + ensure_equals("complex substitution", subresult, "http://Second Life Test/2.0/gaelic/AmigaOS 2.1/mysim/[XXX]"); } - } diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp new file mode 100644 index 0000000000..56cf86f6df --- /dev/null +++ b/indra/newview/tests/llworldmap_test.cpp @@ -0,0 +1,523 @@ +/** + * @file llworldmap_test.cpp + * @author Merov Linden + * @date 2009-03-09 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, 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$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../llworldmap.h" +// Dependencies +#include "../llviewerimagelist.h" +#include "../llworldmapmessage.h" +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +// Stub image calls +LLViewerImageList::LLViewerImageList() { } +LLViewerImageList::~LLViewerImageList() { } +LLViewerImageList gImageList; +LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id, + BOOL usemipmaps, + BOOL level_immediate, + LLGLint internal_format, + LLGLenum primary_format, + LLHost request_from_host) +{ return NULL; } +void LLViewerImage::setBoostLevel(S32 level) { } +void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { } + +// Stub related map calls +LLWorldMapMessage::LLWorldMapMessage() { } +LLWorldMapMessage::~LLWorldMapMessage() { } +void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) { } +void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) { } +LLWorldMipmap::LLWorldMipmap() { } +LLWorldMipmap::~LLWorldMipmap() { } +void LLWorldMipmap::reset() { } +void LLWorldMipmap::dropBoostLevels() { } +void LLWorldMipmap::equalizeBoostLevels() { } +LLPointer<LLViewerImage> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) +{ return NULL; } + +// Stub other stuff +BOOL gPacificDaylightTime; + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +const F32 X_WORLD_TEST = 1000.0f * REGION_WIDTH_METERS; +const F32 Y_WORLD_TEST = 2000.0f * REGION_WIDTH_METERS; +const F32 Z_WORLD_TEST = 240.0f; +const std::string ITEM_NAME_TEST = "Item Foo"; +const std::string TOOLTIP_TEST = "Tooltip Foo"; + +const std::string SIM_NAME_TEST = "Sim Foo"; + +namespace tut +{ + // Test wrapper declarations + struct iteminfo_test + { + // Instance to be tested + LLItemInfo* mItem; + + // Constructor and destructor of the test wrapper + iteminfo_test() + { + LLUUID id; + mItem = new LLItemInfo(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id); + } + ~iteminfo_test() + { + delete mItem; + } + }; + + struct siminfo_test + { + // Instance to be tested + LLSimInfo* mSim; + + // Constructor and destructor of the test wrapper + siminfo_test() + { + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + mSim = new LLSimInfo(handle); + } + ~siminfo_test() + { + delete mSim; + } + }; + + struct worldmap_test + { + // Instance to be tested + LLWorldMap* mWorld; + + // Constructor and destructor of the test wrapper + worldmap_test() + { + mWorld = LLWorldMap::getInstance(); + } + ~worldmap_test() + { + mWorld = NULL; + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<iteminfo_test> iteminfo_t; + typedef iteminfo_t::object iteminfo_object_t; + tut::iteminfo_t tut_iteminfo("iteminfo"); + + typedef test_group<siminfo_test> siminfo_t; + typedef siminfo_t::object siminfo_object_t; + tut::siminfo_t tut_siminfo("siminfo"); + + typedef test_group<worldmap_test> worldmap_t; + typedef worldmap_t::object worldmap_object_t; + tut::worldmap_t tut_worldmap("worldmap"); + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLItemInfo interface + // --------------------------------------------------------------------------------------- + template<> template<> + void iteminfo_object_t::test<1>() + { + // Test 1 : setCount() / getCount() + mItem->setCount(10); + ensure("LLItemInfo::setCount() test failed", mItem->getCount() == 10); + // Test 2 : setTooltip() / getToolTip() + std::string tooltip = TOOLTIP_TEST; + mItem->setTooltip(tooltip); + ensure("LLItemInfo::setTooltip() test failed", mItem->getToolTip() == TOOLTIP_TEST); + // Test 3 : setElevation() / getGlobalPosition() + mItem->setElevation(Z_WORLD_TEST); + LLVector3d pos = mItem->getGlobalPosition(); + LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, Z_WORLD_TEST); + ensure("LLItemInfo::getGlobalPosition() test failed", pos == ref); + // Test 4 : getName() + std::string name = mItem->getName(); + ensure("LLItemInfo::getName() test failed", name == ITEM_NAME_TEST); + // Test 5 : isName() + ensure("LLItemInfo::isName() test failed", mItem->isName(name)); + // Test 6 : getUUID() + LLUUID id; + ensure("LLItemInfo::getUUID() test failed", mItem->getUUID() == id); + // Test 7 : getRegionHandle() + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + ensure("LLItemInfo::getRegionHandle() test failed", mItem->getRegionHandle() == handle); + } + // --------------------------------------------------------------------------------------- + // Test the LLSimInfo interface + // --------------------------------------------------------------------------------------- + // Test Setters and Accessors methods + template<> template<> + void siminfo_object_t::test<1>() + { + // Test 1 : setName() / getName() + std::string name = SIM_NAME_TEST; + mSim->setName(name); + ensure("LLSimInfo::setName() test failed", mSim->getName() == SIM_NAME_TEST); + // Test 2 : isName() + ensure("LLSimInfo::isName() test failed", mSim->isName(name)); + // Test 3 : getGlobalPos() + LLVector3 local; + LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, 0.0f); + LLVector3d pos = mSim->getGlobalPos(local); + ensure("LLSimInfo::getGlobalPos() test failed", pos == ref); + // Test 4 : getGlobalOrigin() + pos = mSim->getGlobalOrigin(); + ensure("LLSimInfo::getGlobalOrigin() test failed", pos == ref); + // Test 5 : clearImage() + try { + mSim->clearImage(); + } catch (...) { + fail("LLSimInfo::clearImage() test failed"); + } + // Test 6 : dropImagePriority() + try { + mSim->dropImagePriority(); + } catch (...) { + fail("LLSimInfo::dropImagePriority() test failed"); + } + // Test 7 : updateAgentCount() + try { + mSim->updateAgentCount(0.0f); + } catch (...) { + fail("LLSimInfo::updateAgentCount() test failed"); + } + // Test 8 : getAgentCount() + S32 agents = mSim->getAgentCount(); + ensure("LLSimInfo::getAgentCount() test failed", agents == 0); + // Test 9 : setLandForSaleImage() / getLandForSaleImage() + LLUUID id; + mSim->setLandForSaleImage(id); + LLPointer<LLViewerImage> image = mSim->getLandForSaleImage(); + ensure("LLSimInfo::getLandForSaleImage() test failed", image.isNull()); + // Test 10 : isPG() + mSim->setAccess(SIM_ACCESS_PG); + ensure("LLSimInfo::isPG() test failed", mSim->isPG()); + // Test 11 : isDown() + mSim->setAccess(SIM_ACCESS_DOWN); + ensure("LLSimInfo::isDown() test failed", mSim->isDown()); + // Test 12 : Access strings can't be accessed from unit test... + //ensure("LLSimInfo::getAccessString() test failed", mSim->getAccessString() == "Offline"); + // Test 13 : Region strings can't be accessed from unit test... + //mSim->setRegionFlags(REGION_FLAGS_SANDBOX); + //ensure("LLSimInfo::setRegionFlags() test failed", mSim->getFlagsString() == "Sandbox"); + } + // Test management of LLInfoItem lists + template<> template<> + void siminfo_object_t::test<2>() + { + // Test 14 : clearItems() + try { + mSim->clearItems(); + } catch (...) { + fail("LLSimInfo::clearItems() at init test failed"); + } + + // Test 15 : Verify that all the lists are empty + LLSimInfo::item_info_list_t list; + list = mSim->getTeleHub(); + ensure("LLSimInfo::getTeleHub() empty at init test failed", list.empty()); + list = mSim->getInfoHub(); + ensure("LLSimInfo::getInfoHub() empty at init test failed", list.empty()); + list = mSim->getPGEvent(); + ensure("LLSimInfo::getPGEvent() empty at init test failed", list.empty()); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::getMatureEvent() empty at init test failed", list.empty()); + list = mSim->getLandForSale(); + ensure("LLSimInfo::getLandForSale() empty at init test failed", list.empty()); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::getAgentLocation() empty at init test failed", list.empty()); + + // Create an item to be inserted + LLUUID id; + LLItemInfo item(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id); + + // Insert the item in each list + mSim->insertTeleHub(item); + mSim->insertInfoHub(item); + mSim->insertPGEvent(item); + mSim->insertMatureEvent(item); + mSim->insertLandForSale(item); + mSim->insertAgentLocation(item); + + // Test 16 : Verify that the lists contain 1 item each + list = mSim->getTeleHub(); + ensure("LLSimInfo::insertTeleHub() test failed", list.size() == 1); + list = mSim->getInfoHub(); + ensure("LLSimInfo::insertInfoHub() test failed", list.size() == 1); + list = mSim->getPGEvent(); + ensure("LLSimInfo::insertPGEvent() test failed", list.size() == 1); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::insertMatureEvent() test failed", list.size() == 1); + list = mSim->getLandForSale(); + ensure("LLSimInfo::insertLandForSale() test failed", list.size() == 1); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::insertAgentLocation() test failed", list.size() == 1); + + // Test 17 : clearItems() + try { + mSim->clearItems(); + } catch (...) { + fail("LLSimInfo::clearItems() at end test failed"); + } + + // Test 18 : Verify that all the lists are empty again... *except* agent which is persisted!! (on purpose) + list = mSim->getTeleHub(); + ensure("LLSimInfo::getTeleHub() empty after clear test failed", list.empty()); + list = mSim->getInfoHub(); + ensure("LLSimInfo::getInfoHub() empty after clear test failed", list.empty()); + list = mSim->getPGEvent(); + ensure("LLSimInfo::getPGEvent() empty after clear test failed", list.empty()); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::getMatureEvent() empty after clear test failed", list.empty()); + list = mSim->getLandForSale(); + ensure("LLSimInfo::getLandForSale() empty after clear test failed", list.empty()); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::getAgentLocation() empty after clear test failed", list.size() == 1); + } + + // --------------------------------------------------------------------------------------- + // Test the LLWorldMap interface + // --------------------------------------------------------------------------------------- + // Test Setters and Accessors methods + template<> template<> + void worldmap_object_t::test<1>() + { + // Test 1 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at init test failed"); + } + // Test 2 : clearImageRefs() + try { + mWorld->clearImageRefs(); + } catch (...) { + fail("LLWorldMap::clearImageRefs() test failed"); + } + // Test 3 : dropImagePriorities() + try { + mWorld->dropImagePriorities(); + } catch (...) { + fail("LLWorldMap::dropImagePriorities() test failed"); + } + // Test 4 : reloadItems() + try { + mWorld->reloadItems(true); + } catch (...) { + fail("LLWorldMap::reloadItems() test failed"); + } + // Test 5 : updateRegions() + try { + mWorld->updateRegions(1000, 1000, 1004, 1004); + } catch (...) { + fail("LLWorldMap::updateRegions() test failed"); + } + // Test 6 : equalizeBoostLevels() + try { + mWorld->equalizeBoostLevels(); + } catch (...) { + fail("LLWorldMap::equalizeBoostLevels() test failed"); + } + // Test 7 : getObjectsTile() + try { + LLPointer<LLViewerImage> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); + ensure("LLWorldMap::getObjectsTile() failed", image.isNull()); + } catch (...) { + fail("LLWorldMap::getObjectsTile() test failed with exception"); + } + } + // Test management of LLSimInfo lists + template<> template<> + void worldmap_object_t::test<2>() + { + // Test 8 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at init test failed"); + } + + // Test 9 : Verify that all the region list is empty + LLWorldMap::sim_info_map_t list; + list = mWorld->getRegionMap(); + ensure("LLWorldMap::getRegionMap() empty at init test failed", list.empty()); + + // Test 10 : Insert a region + bool success; + LLUUID id; + std::string name_sim = SIM_NAME_TEST; + success = mWorld->insertRegion( U32(X_WORLD_TEST), + U32(Y_WORLD_TEST), + name_sim, + id, + SIM_ACCESS_PG, + REGION_FLAGS_SANDBOX); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertRegion() failed", success && (list.size() == 1)); + + // Test 11 : Insert an item in the same region -> number of regions doesn't increase + std::string name_item = ITEM_NAME_TEST; + success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS/2), + U32(Y_WORLD_TEST + REGION_WIDTH_METERS/2), + name_item, + id, + MAP_ITEM_LAND_FOR_SALE, + 0, 0); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertItem() in existing region failed", success && (list.size() == 1)); + + // Test 12 : Insert an item in another region -> number of regions increases + success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS*2), + U32(Y_WORLD_TEST + REGION_WIDTH_METERS*2), + name_item, + id, + MAP_ITEM_LAND_FOR_SALE, + 0, 0); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertItem() in unexisting region failed", success && (list.size() == 2)); + + // Test 13 : simInfoFromPosGlobal() in region + LLVector3d pos1( X_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2, + 0.0f); + LLSimInfo* sim; + sim = mWorld->simInfoFromPosGlobal(pos1); + ensure("LLWorldMap::simInfoFromPosGlobal() test on existing region failed", sim != NULL); + + // Test 14 : simInfoFromPosGlobal() outside region + LLVector3d pos2( X_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2, + 0.0f); + sim = mWorld->simInfoFromPosGlobal(pos2); + ensure("LLWorldMap::simInfoFromPosGlobal() test outside region failed", sim == NULL); + + // Test 15 : simInfoFromName() + sim = mWorld->simInfoFromName(name_sim); + ensure("LLWorldMap::simInfoFromName() test on existing region failed", sim != NULL); + + // Test 16 : simInfoFromHandle() + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + sim = mWorld->simInfoFromHandle(handle); + ensure("LLWorldMap::simInfoFromHandle() test on existing region failed", sim != NULL); + + // Test 17 : simNameFromPosGlobal() + LLVector3d pos3( X_WORLD_TEST + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS/2, + 0.0f); + success = mWorld->simNameFromPosGlobal(pos3, name_sim); + ensure("LLWorldMap::simNameFromPosGlobal() test on existing region failed", success && (name_sim == SIM_NAME_TEST)); + + // Test 18 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at end test failed"); + } + + // Test 19 : Verify that all the region list is empty + list = mWorld->getRegionMap(); + ensure("LLWorldMap::getRegionMap() empty at end test failed", list.empty()); + } + // Test tracking + template<> template<> + void worldmap_object_t::test<3>() + { + // Point to track + LLVector3d pos( X_WORLD_TEST + REGION_WIDTH_METERS/2, Y_WORLD_TEST + REGION_WIDTH_METERS/2, Z_WORLD_TEST); + + // Test 20 : no tracking + mWorld->cancelTracking(); + ensure("LLWorldMap::cancelTracking() at begin test failed", mWorld->isTracking() == false); + + // Test 21 : set tracking + mWorld->setTracking(pos); + ensure("LLWorldMap::setTracking() failed", mWorld->isTracking() && !mWorld->isTrackingValidLocation()); + + // Test 22 : set click and commit flags + mWorld->setTrackingDoubleClick(); + ensure("LLWorldMap::setTrackingDoubleClick() failed", mWorld->isTrackingDoubleClick()); + mWorld->setTrackingCommit(); + ensure("LLWorldMap::setTrackingCommit() failed", mWorld->isTrackingCommit()); + + // Test 23 : in rectangle test + bool inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST, Y_WORLD_TEST, + X_WORLD_TEST + REGION_WIDTH_METERS, + Y_WORLD_TEST + REGION_WIDTH_METERS); + ensure("LLWorldMap::isTrackingInRectangle() in rectangle failed", inRect); + inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST + REGION_WIDTH_METERS, + Y_WORLD_TEST + REGION_WIDTH_METERS, + X_WORLD_TEST + 2 * REGION_WIDTH_METERS, + Y_WORLD_TEST + 2 * REGION_WIDTH_METERS); + ensure("LLWorldMap::isTrackingInRectangle() outside rectangle failed", !inRect); + + // Test 24 : set tracking to valid and invalid + mWorld->setTrackingValid(); + ensure("LLWorldMap::setTrackingValid() failed", mWorld->isTrackingValidLocation() && !mWorld->isTrackingInvalidLocation()); + mWorld->setTrackingInvalid(); + ensure("LLWorldMap::setTrackingInvalid() failed", !mWorld->isTrackingValidLocation() && mWorld->isTrackingInvalidLocation()); + + // Test 25 : getTrackedPositionGlobal() + LLVector3d res = mWorld->getTrackedPositionGlobal(); + ensure("LLWorldMap::getTrackedPositionGlobal() failed", res == pos); + + // Test 26 : reset tracking + mWorld->cancelTracking(); + ensure("LLWorldMap::cancelTracking() at end test failed", mWorld->isTracking() == false); + } +} diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp new file mode 100644 index 0000000000..9938175c55 --- /dev/null +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -0,0 +1,176 @@ +/** + * @file llworldmipmap_test.cpp + * @author Merov Linden + * @date 2009-02-03 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, 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$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../llworldmipmap.h" +// Dependencies +#include "../llviewerimagelist.h" +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +LLViewerImageList::LLViewerImageList() { } +LLViewerImageList::~LLViewerImageList() { } + +LLViewerImageList gImageList; + +LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url, + BOOL usemipmaps, + BOOL level_immediate, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id) +{ return NULL; } +void LLViewerImage::setBoostLevel(S32 level) { } + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ + // Test wrapper declaration + struct worldmipmap_test + { + // Derived test class + class LLTestWorldMipmap : public LLWorldMipmap + { + // Put here stubbs of virtual methods we shouldn't call all the way down + }; + // Instance to be tested + LLTestWorldMipmap* mMap; + + // Constructor and destructor of the test wrapper + worldmipmap_test() + { + mMap = new LLTestWorldMipmap; + } + ~worldmipmap_test() + { + delete mMap; + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<worldmipmap_test> worldmipmap_t; + typedef worldmipmap_t::object worldmipmap_object_t; + tut::worldmipmap_t tut_worldmipmap("worldmipmap"); + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + // Test static methods + // Test 1 : scaleToLevel() + template<> template<> + void worldmipmap_object_t::test<1>() + { + S32 level = mMap->scaleToLevel(0.0); + ensure("scaleToLevel() test 1 failed", level == LLWorldMipmap::MAP_LEVELS); + level = mMap->scaleToLevel(LLWorldMipmap::MAP_TILE_SIZE); + ensure("scaleToLevel() test 2 failed", level == 1); + level = mMap->scaleToLevel(10 * LLWorldMipmap::MAP_TILE_SIZE); + ensure("scaleToLevel() test 3 failed", level == 1); + } + // Test 2 : globalToMipmap() + template<> template<> + void worldmipmap_object_t::test<2>() + { + U32 grid_x, grid_y; + mMap->globalToMipmap(1000.f*REGION_WIDTH_METERS, 1000.f*REGION_WIDTH_METERS, 1, &grid_x, &grid_y); + ensure("globalToMipmap() test 1 failed", (grid_x == 1000) && (grid_y == 1000)); + mMap->globalToMipmap(0.0, 0.0, LLWorldMipmap::MAP_LEVELS, &grid_x, &grid_y); + ensure("globalToMipmap() test 2 failed", (grid_x == 0) && (grid_y == 0)); + } + // Test 3 : getObjectsTile() + template<> template<> + void worldmipmap_object_t::test<3>() + { + // Depends on some inline methods in LLViewerImage... Thinking about how to make this work + // LLPointer<LLViewerImage> img = mMap->getObjectsTile(0, 0, 1); + // ensure("getObjectsTile() test failed", img.isNull()); + } + // Test 4 : equalizeBoostLevels() + template<> template<> + void worldmipmap_object_t::test<4>() + { + try + { + mMap->equalizeBoostLevels(); + } + catch (...) + { + fail("equalizeBoostLevels() test failed"); + } + } + // Test 5 : dropBoostLevels() + template<> template<> + void worldmipmap_object_t::test<5>() + { + try + { + mMap->dropBoostLevels(); + } + catch (...) + { + fail("dropBoostLevels() test failed"); + } + } + // Test 6 : reset() + template<> template<> + void worldmipmap_object_t::test<6>() + { + try + { + mMap->reset(); + } + catch (...) + { + fail("reset() test failed"); + } + } +} diff --git a/indra/newview/tests/llxmlrpclistener_test.cpp b/indra/newview/tests/llxmlrpclistener_test.cpp new file mode 100644 index 0000000000..c2c7e963b9 --- /dev/null +++ b/indra/newview/tests/llxmlrpclistener_test.cpp @@ -0,0 +1,230 @@ +/* + * @file llxmlrpclistener_test.cpp + * @author Nat Goodspeed + * @date 2009-03-20 + * @brief Test for llxmlrpclistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "../llviewerprecompiledheaders.h" +// associated header +#include "../llxmlrpclistener.h" +// STL headers +#include <iomanip> +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../llxmlrpctransaction.h" +#include "llevents.h" +#include "lleventfilter.h" +#include "llsd.h" +#include "llcontrol.h" +#include "tests/wrapllerrs.h" + +LLControlGroup gSavedSettings("Global"); + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct data + { + data(): + pumps(LLEventPumps::instance()), + uri("http://127.0.0.1:8000") + { + // These variables are required by machinery used by + // LLXMLRPCTransaction. The values reflect reality for this test + // executable; hopefully these values are correct. + gSavedSettings.declareBOOL("BrowserProxyEnabled", FALSE, "", FALSE); // don't persist + gSavedSettings.declareBOOL("NoVerifySSLCert", TRUE, "", FALSE); // don't persist + } + + // LLEventPump listener signature + bool captureReply(const LLSD& r) + { + reply = r; + return false; + } + + LLSD reply; + LLEventPumps& pumps; + std::string uri; + }; + typedef test_group<data> llxmlrpclistener_group; + typedef llxmlrpclistener_group::object object; + llxmlrpclistener_group llxmlrpclistenergrp("llxmlrpclistener"); + + template<> template<> + void object::test<1>() + { + set_test_name("request validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "missing params"); + ensure_contains("identified missing", threw, "method"); + ensure_contains("identified missing", threw, "reply"); + } + + template<> template<> + void object::test<2>() + { + set_test_name("param types validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"]["specifically"] = "world"; // LLXMLRPCListener only handles scalar params + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "unknown type"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("success case"); + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["responses"]["hi_there"].asString(), "Hello, world!"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("bogus method"); + LLSD request; + request["uri"] = uri; + request["method"] = "goodbye"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals("XMLRPC error", reply["status"].asString(), "XMLRPCError"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("bad type"); + LLSD request; + request["uri"] = uri; + request["method"] = "getdict"; + request["reply"] = "reply"; + (void)request["params"]; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["status"].asString(), "BadType"); + ensure_contains("bad type", reply["responses"]["nested_dict"].asString(), "bad XMLRPC type"); + } +} // namespace tut + +/***************************************************************************** +* Resolve link errors: use real machinery here, since we intend to exchange +* actual XML with a peer process. +*****************************************************************************/ +// Including llxmlrpctransaction.cpp drags in the static LLXMLRPCListener +// instantiated there. That's why it works to post requests to the LLEventPump +// named "LLXMLRPCTransaction". +#include "../llxmlrpctransaction.cpp" +#include "llcontrol.cpp" +#include "llxmltree.cpp" +#include "llxmlparser.cpp" diff --git a/indra/newview/tests/test_llxmlrpc_peer.py b/indra/newview/tests/test_llxmlrpc_peer.py new file mode 100644 index 0000000000..cb8f7d26c4 --- /dev/null +++ b/indra/newview/tests/test_llxmlrpc_peer.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +"""\ +@file test_llxmlrpc_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewergpl$ +Copyright (c) 2008, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys +from threading import Thread +from SimpleXMLRPCServer import SimpleXMLRPCServer + +mydir = os.path.dirname(__file__) # expected to be .../indra/newview/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +sys.path.insert(1, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) +from testrunner import run, debug + +class TestServer(SimpleXMLRPCServer): + def _dispatch(self, method, params): + try: + func = getattr(self, method) + except AttributeError: + raise Exception('method "%s" is not supported' % method) + else: + # LLXMLRPCListener constructs XMLRPC parameters that arrive as a + # 1-tuple containing a dict. + return func(**(params[0])) + + def hello(self, who): + # LLXMLRPCListener expects a dict return. + return {"hi_there": "Hello, %s!" % who} + + def getdict(self): + return dict(nested_dict=dict(a=17, b=5)) + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class ServerRunner(Thread): + def run(self): + server = TestServer(('127.0.0.1', 8000)) + debug("Starting XMLRPC server...\n") + server.serve_forever() + +if __name__ == "__main__": + sys.exit(run(server=ServerRunner(name="xmlrpc"), *sys.argv[1:])) |