/** * @file llmediadataclient_test.cpp * @brief LLMediaDatClient tests * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "../llviewerprecompiledheaders.h" #include <iostream> #include "../test/lltut.h" #include "llsdserialize.h" #include "llsdutil.h" #include "llerrorcontrol.h" #include "llhttpconstants.h" #include "../llmediadataclient.h" #include "../llvovolume.h" #include "../../llprimitive/llmediaentry.cpp" #include "../../llprimitive/llmaterialid.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>http://foo.example.com</string> \ <string>http://bar.example.com</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; #if 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 ) { LLSD content; content["reason"] = "fake reason"; responder->failureResult(HTTP_SERVICE_UNAVAILABLE, "fake reason", content); return; } else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR) { LLSD error; error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE; result["error"] = error; } responder->successResult(result); } #endif 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 bool isCurrentMediaUrl(U8 index, const std::string &url) const { return (mRep["media_data"][(LLSD::Integer)index].asString() == url); } 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("LLMediaDataClient"); 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); 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); // and mark the second and fourth ones dead. Call removeFromQueue when marking dead, since this is what LLVOVolume will do. dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o2))->markDead(); mdc->removeFromQueue(o2); dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o4))->markDead(); mdc->removeFromQueue(o4); // The removeFromQueue calls should remove the second and fourth ones ensure("is in queue 1", mdc->isInQueue(o1)); ensure("is not in queue 2", !mdc->isInQueue(o2)); ensure("is in queue 3", mdc->isInQueue(o3)); ensure("is not in queue 4", !mdc->isInQueue(o4)); ensure("post records", gPostRecords->size(), 0); ::pump_timers(); // The first tick should process the first item ensure("is not in queue 1", !mdc->isInQueue(o1)); ensure("is not in queue 2", !mdc->isInQueue(o2)); ensure("is in queue 3", mdc->isInQueue(o3)); ensure("is not in queue 4", !mdc->isInQueue(o4)); ensure("post records", gPostRecords->size(), 1); ::pump_timers(); // The second tick should process the third, emptying the queue ensure("is not in queue 3", !mdc->isInQueue(o3)); 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, 3, 1, 4 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 in queue 1", mdc->isInQueue(o1)); 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) + ". 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 3 ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1)); ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); ensure(STR(tick_num) + ". is not 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_3)); ::pump_timers(); ++tick_num; // 3 The third tick should remove object 1 ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2)); ensure(STR(tick_num) + ". is not 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 4 ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); ensure(STR(tick_num) + ". is not 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(), 4); ensure(STR(tick_num) + ". post object id", (*gPostRecords)[3]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4)); ::pump_timers(); ++tick_num; // 5 The fifth tick should not change the state of anything. ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1)); ensure(STR(tick_num) + ". is not 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(), 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 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); } template<> template<> void mediadataclient_object_t::test<13>() { // // Test supression of redundant navigates. // LOG_TEST(13); LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true")); { LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD); const char *TEST_URL = "http://foo.example.com"; const char *TEST_URL_2 = "http://example.com"; mdc->navigate(o1, 0, TEST_URL); mdc->navigate(o1, 1, TEST_URL); mdc->navigate(o1, 0, TEST_URL_2); mdc->navigate(o1, 1, TEST_URL_2); // This should add two requests to the queue, one for face 0 of the object and one for face 1. ensure("before pump: 1 is in queue", mdc->isInQueue(o1)); ::pump_timers(); ensure("after first pump: 1 is in queue", mdc->isInQueue(o1)); ::pump_timers(); ensure("after second pump: 1 is not in queue", !mdc->isInQueue(o1)); ensure("first post has correct url", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY].asString(), std::string(TEST_URL_2)); ensure("second post has correct url", (*gPostRecords)[1]["body"][LLMediaEntry::CURRENT_URL_KEY].asString(), std::string(TEST_URL_2)); } } }