Unit tests for LLMediaDataClient

This required a bit of refactoring of LLMediaDataClient:
- Created LLMediaDataClientObject ABC, which now has a
  concrete impl in LLVOVolume
- Created unit test with 6 tests (for now), testing
  - LLObjectMediaDataClient::fetchMedia()
  - LLObjectMediaDataClient::updateMedia()
  - LLObjectMediaNavigateClient::navigate()
  - queue ordering
  - retries
  - nav bounce back
- Also ensures that ref counting works properly (this is important, because
  ownership is tricky with smart pointers put into queues, peeled off
  into timers that fire and auto destruct, and HTTP responders that also
  auto-destruct)
- Had to fix LLCurl::Responder's stub, which was not initializing
  the ref count to 0, causing the ref counting tests to fail
  (boy, that was hard to find!).

Reviewed by Callum
meow-7.2.2
Rick Pasetto 2009-10-09 18:56:36 -07:00
parent 8d1f3f7351
commit d4b2897700
8 changed files with 713 additions and 83 deletions

View File

@ -22,6 +22,7 @@
#include "linden_common.h"
LLCurl::Responder::Responder()
: mReferenceCount(0)
{
}

View File

@ -1540,6 +1540,7 @@ include(LLAddBuildTest)
SET(viewer_TEST_SOURCE_FILES
llagentaccess.cpp
lldateutil.cpp
llmediadataclient.cpp
llviewerhelputil.cpp
)
set_source_files_properties(

View File

@ -5294,6 +5294,17 @@
<key>Value</key>
<integer>13</integer>
</map>
<key>PrimMediaMaxRetries</key>
<map>
<key>Comment</key>
<string>Maximum number of retries for media queries.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>4</integer>
</map>
<key>PrimMediaRequestQueueDelay</key>
<map>
<key>Comment</key>

View File

@ -41,7 +41,6 @@
#include "llmediaentry.h"
#include "lltextureentry.h"
#include "llviewerregion.h"
#include "llvovolume.h"
//
// When making a request
@ -54,7 +53,9 @@
// - Any request that gets a 503 still goes through the retry logic
//
// Some helpful logging macros
const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs
const U32 LLMediaDataClient::MAX_RETRIES = 4;
//////////////////////////////////////////////////////////////////////////////////////
//
@ -65,7 +66,7 @@
LLMediaDataClient::Request::Request(const std::string &cap_name,
const LLSD& sd_payload,
LLVOVolume *obj,
LLMediaDataClientObject *obj,
LLMediaDataClient *mdc)
: mCapName(cap_name),
mPayload(sd_payload),
@ -78,6 +79,7 @@ LLMediaDataClient::Request::Request(const std::string &cap_name,
LLMediaDataClient::Request::~Request()
{
LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;
mMDC = NULL;
mObject = NULL;
}
@ -85,7 +87,7 @@ LLMediaDataClient::Request::~Request()
std::string LLMediaDataClient::Request::getCapability() const
{
return getObject()->getRegion()->getCapability(getCapName());
return getObject()->getCapabilityUrl(getCapName());
}
// Helper function to get the "type" of request, which just pokes around to
@ -137,6 +139,17 @@ void LLMediaDataClient::Request::reEnqueue() const
mMDC->enqueue(this);
}
F32 LLMediaDataClient::Request::getRetryTimerDelay() const
{
return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
mMDC->mRetryTimerDelay;
}
U32 LLMediaDataClient::Request::getMaxNumRetries() const
{
return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
}
std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
{
s << "<request>"
@ -163,6 +176,7 @@ LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
// virtual
LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
mResponder = NULL;
}
@ -190,28 +204,31 @@ LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
LLMediaDataClient::Responder::~Responder()
{
LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;
mRequest = NULL;
}
/*virtual*/
void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
{
extern LLControlGroup gSavedSettings;
if (status == HTTP_SERVICE_UNAVAILABLE)
{
F32 retry_timeout = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
if (retry_timeout <= 0.0)
{
retry_timeout = (F32)UNAVAILABLE_RETRY_TIMER_DELAY;
}
LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
F32 retry_timeout = mRequest->getRetryTimerDelay();
mRequest->incRetryCount();
// Start timer (instances are automagically tracked by
// InstanceTracker<> and LLEventTimer)
new RetryTimer(F32(retry_timeout/*secs*/), this);
if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
{
LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
// Start timer (instances are automagically tracked by
// InstanceTracker<> and LLEventTimer)
new RetryTimer(F32(retry_timeout/*secs*/), this);
}
else {
LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " <<
mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
}
}
else {
std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
@ -252,29 +269,25 @@ bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const r
// Calculate the scores for each.
F64 o1_score = Comparator::getObjectScore(o1->getObject());
F64 o2_score = Comparator::getObjectScore(o2->getObject());
return ( o1_score > o2_score );
// XXX Weird: a higher score should go earlier, but by observation I notice
// that this causes further-away objects load first. This is counterintuitive
// to the priority_queue Comparator, which states that this function should
// return 'true' if o1 should be *before* o2.
// In other words, I'd have expected that the following should return
// ( o1_score > o2_score).
return ( o1_score < o2_score );
}
// static
F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj)
F64 LLMediaDataClient::Comparator::getObjectScore(const LLMediaDataClientObject::ptr_t &obj)
{
// *TODO: make this less expensive?
F32 dist = obj->getRenderPosition().length() + 0.1; // avoids div by 0
F64 dist = obj->getDistanceFromAvatar() + 0.1; // avoids div by 0
// square the distance so that they are in the same "unit magnitude" as
// the interest (which is an area)
dist *= dist;
F64 interest = (F64)1;
int i = 0;
int end = obj->getNumTEs();
for ( ; i < end; ++i)
{
const viewer_media_t &impl = obj->getMediaImpl(i);
if (!impl.isNull())
{
interest += impl->getInterest();
}
}
F64 interest = obj->getTotalMediaInterest() + 1.0;
return interest/dist;
}
@ -282,7 +295,7 @@ F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj)
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::PriorityQueue
// Queue of LLVOVolume smart pointers to request media for.
// Queue of LLMediaDataClientObject smart pointers to request media for.
//
//////////////////////////////////////////////////////////////////////////////////////
@ -304,7 +317,7 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::QueueTimer
// Queue of LLVOVolume smart pointers to request media for.
// Queue of LLMediaDataClientObject smart pointers to request media for.
//
//////////////////////////////////////////////////////////////////////////////////////
@ -316,6 +329,7 @@ LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
LLMediaDataClient::QueueTimer::~QueueTimer()
{
LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;
mMDC->setIsRunning(false);
mMDC = NULL;
}
@ -343,10 +357,10 @@ BOOL LLMediaDataClient::QueueTimer::tick()
// Peel one off of the items from the queue, and execute request
request_ptr_t request = queue.top();
llassert(!request.isNull());
const ll_vo_volume_ptr_t &object = request->getObject();
const LLMediaDataClientObject *object = request->getObject();
bool performed_request = false;
llassert(!object.isNull());
if (!object.isNull() && object->hasMedia())
llassert(NULL != object);
if (NULL != object && object->hasMedia())
{
std::string url = request->getCapability();
if (!url.empty())
@ -368,12 +382,13 @@ BOOL LLMediaDataClient::QueueTimer::tick()
LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL;
}
}
bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES;
bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries;
if (performed_request || exceeded_retries) // Try N times before giving up
{
if (exceeded_retries)
{
LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << LLMediaDataClient::MAX_RETRIES << " tries...popping object id " << object->getID() << LL_ENDL;
LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
<< mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
// XXX Should we bring up a warning dialog??
}
queue.pop();
@ -390,15 +405,9 @@ void LLMediaDataClient::startQueueTimer()
{
if (! mQueueTimerIsRunning)
{
extern LLControlGroup gSavedSettings;
F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
if (queue_timer_delay <= 0.0f)
{
queue_timer_delay = (F32)LLMediaDataClient::QUEUE_TIMER_DELAY;
}
LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << queue_timer_delay << " seconds)" << LL_ENDL;
LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
// LLEventTimer automagically takes care of the lifetime of this object
new QueueTimer(queue_timer_delay, this);
new QueueTimer(mQueueTimerDelay, this);
}
}
@ -407,9 +416,9 @@ void LLMediaDataClient::stopQueueTimer()
mQueueTimerIsRunning = false;
}
void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload)
void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
{
if (NULL == object || ! object->hasMedia()) return;
if (object.isNull() || ! object->hasMedia()) return;
// Push the object on the priority queue
enqueue(new Request(getCapabilityName(), payload, object, this));
@ -432,7 +441,13 @@ void LLMediaDataClient::enqueue(const Request *request)
//
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::LLMediaDataClient()
LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
F32 retry_timer_delay,
U32 max_retries)
: mQueueTimerDelay(queue_timer_delay),
mRetryTimerDelay(retry_timer_delay),
mMaxNumRetries(max_retries),
mQueueTimerIsRunning(false)
{
pRequestQueue = new PriorityQueue();
}
@ -442,12 +457,17 @@ LLMediaDataClient::~LLMediaDataClient()
stopQueueTimer();
// This should clear the queue, and hopefully call all the destructors.
LL_DEBUGS("LLMediaDataClient") << "destructor: queue: " <<
LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
(pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL;
delete pRequestQueue;
pRequestQueue = NULL;
}
bool LLMediaDataClient::isEmpty() const
{
return (NULL == pRequestQueue) ? true : pRequestQueue->empty();
}
//////////////////////////////////////////////////////////////////////////////////////
//
// LLObjectMediaDataClient
@ -465,7 +485,7 @@ const char *LLObjectMediaDataClient::getCapabilityName() const
return "ObjectMedia";
}
void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object)
void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object)
{
LLSD sd_payload;
sd_payload["verb"] = "GET";
@ -473,18 +493,17 @@ void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object)
request(object, sd_payload);
}
void LLObjectMediaDataClient::updateMedia(LLVOVolume *object)
void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)
{
LLSD sd_payload;
sd_payload["verb"] = "UPDATE";
sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
LLSD object_media_data;
for (int i=0; i < object->getNumTEs(); i++) {
LLTextureEntry *texture_entry = object->getTE(i);
llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia());
const LLSD &media_data =
(texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD();
object_media_data.append(media_data);
int i = 0;
int end = object->getMediaDataCount();
for ( ; i < end ; ++i)
{
object_media_data.append(object->getMediaDataLLSD(i));
}
sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
@ -548,7 +567,7 @@ const char *LLObjectMediaNavigateClient::getCapabilityName() const
return "ObjectMediaNavigate";
}
void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url)
void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url)
{
LLSD sd_payload;
sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();

View File

@ -36,12 +36,36 @@
#include "llhttpclient.h"
#include <queue>
#include "llrefcount.h"
#include "llpointer.h"
#include "lltimer.h"
// Forward decls
class LLVOVolume;
typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t;
// Link seam for LLVOVolume
class LLMediaDataClientObject : public LLRefCount
{
public:
// Get the number of media data items
virtual U8 getMediaDataCount() const = 0;
// Get the media data at index, as an LLSD
virtual LLSD getMediaDataLLSD(U8 index) const = 0;
// Get this object's UUID
virtual LLUUID getID() const = 0;
// Navigate back to previous URL
virtual void mediaNavigateBounceBack(U8 index) = 0;
// Does this object have media?
virtual bool hasMedia() const = 0;
// Update the object's media data to the given array
virtual void updateObjectMediaData(LLSD const &media_data_array) = 0;
// Return the distance from the object to the avatar
virtual F64 getDistanceFromAvatar() const = 0;
// Return the total "interest" of the media (on-screen area)
virtual F64 getTotalMediaInterest() const = 0;
// Return the given cap url
virtual std::string getCapabilityUrl(const std::string &name) const = 0;
// smart pointer
typedef LLPointer<LLMediaDataClientObject> ptr_t;
};
// This object creates a priority queue for requests.
// Abstracts the Cap URL, the request, and the responder
@ -50,15 +74,23 @@ class LLMediaDataClient : public LLRefCount
public:
LOG_CLASS(LLMediaDataClient);
const static int QUEUE_TIMER_DELAY = 1; // seconds(s)
const static int MAX_RETRIES = 4;
const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s)
const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs
const static U32 MAX_RETRIES;// = 4;
// Constructor
LLMediaDataClient();
LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES);
// Make the request
void request(LLVOVolume *object, const LLSD &payload);
void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload);
F32 getRetryTimerDelay() const { return mRetryTimerDelay; }
// Returns true iff the queue is empty
bool isEmpty() const;
protected:
// Destructor
virtual ~LLMediaDataClient(); // use unref
@ -73,10 +105,10 @@ protected:
NAVIGATE
};
Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc);
Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
const std::string &getCapName() const { return mCapName; }
const LLSD &getPayload() const { return mPayload; }
LLVOVolume *getObject() const { return mObject; }
LLMediaDataClientObject *getObject() const { return mObject; }
U32 getNum() const { return mNum; }
@ -92,6 +124,9 @@ protected:
// Re-enqueue thyself
void reEnqueue() const;
F32 getRetryTimerDelay() const;
U32 getMaxNumRetries() const;
public:
friend std::ostream& operator<<(std::ostream &s, const Request &q);
@ -101,7 +136,7 @@ protected:
private:
std::string mCapName;
LLSD mPayload;
ll_vo_volume_ptr_t mObject;
LLMediaDataClientObject::ptr_t mObject;
// Simple tracking
const U32 mNum;
static U32 sNum;
@ -115,8 +150,6 @@ protected:
// Responder
class Responder : public LLHTTPClient::Responder
{
static const int UNAVAILABLE_RETRY_TIMER_DELAY = 5; // secs
public:
Responder(const request_ptr_t &request);
//If we get back an error (not found, etc...), handle it here
@ -163,7 +196,7 @@ private:
public:
bool operator() (const request_ptr_t &o1, const request_ptr_t &o2) const;
private:
static F64 getObjectScore(const ll_vo_volume_ptr_t &obj);
static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj);
};
// PriorityQueue
@ -194,6 +227,10 @@ private:
void startQueueTimer();
void stopQueueTimer();
void setIsRunning(bool val) { mQueueTimerIsRunning = val; }
const F32 mQueueTimerDelay;
const F32 mRetryTimerDelay;
const U32 mMaxNumRetries;
bool mQueueTimerIsRunning;
@ -205,11 +242,15 @@ private:
class LLObjectMediaDataClient : public LLMediaDataClient
{
public:
LLObjectMediaDataClient() {}
LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
{}
~LLObjectMediaDataClient() {}
void fetchMedia(LLVOVolume *object);
void updateMedia(LLVOVolume *object);
void fetchMedia(LLMediaDataClientObject *object);
void updateMedia(LLMediaDataClientObject *object);
protected:
// Subclasses must override this factory method to return a new responder
@ -231,14 +272,18 @@ protected:
// MediaDataResponder specific for the ObjectMediaNavigate cap
class LLObjectMediaNavigateClient : public LLMediaDataClient
{
public:
// NOTE: from llmediaservice.h
static const int ERROR_PERMISSION_DENIED_CODE = 8002;
public:
LLObjectMediaNavigateClient() {}
LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
{}
~LLObjectMediaNavigateClient() {}
void navigate(LLVOVolume *object, U8 texture_index, const std::string &url);
void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);
protected:
// Subclasses must override this factory method to return a new responder

View File

@ -91,6 +91,57 @@ LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient =
static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
// Implementation class of LLMediaDataClientObject. See llmediadataclient.h
class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
{
public:
LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {}
LLMediaDataClientObjectImpl() { mObject = NULL; }
virtual U8 getMediaDataCount() const
{ return mObject->getNumTEs(); }
virtual LLSD getMediaDataLLSD(U8 index) const
{
LLSD result;
LLTextureEntry *te = mObject->getTE(index);
if (NULL != te)
{
llassert((te->getMediaData() != NULL) == te->hasMedia());
if (te->getMediaData() != NULL)
{
result = te->getMediaData()->asLLSD();
}
}
return result;
}
virtual LLUUID getID() const
{ return mObject->getID(); }
virtual void mediaNavigateBounceBack(U8 index)
{ mObject->mediaNavigateBounceBack(index); }
virtual bool hasMedia() const
{ return mObject->hasMedia(); }
virtual void updateObjectMediaData(LLSD const &data)
{ mObject->updateObjectMediaData(data); }
virtual F64 getDistanceFromAvatar() const
{ return mObject->getRenderPosition().length(); }
virtual F64 getTotalMediaInterest() const
{ return mObject->getTotalMediaInterest(); }
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mObject->getRegion()->getCapability(name); }
private:
LLPointer<LLVOVolume> mObject;
};
LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
: LLViewerObject(id, pcode, regionp),
mVolumeImpl(NULL)
@ -134,8 +185,12 @@ LLVOVolume::~LLVOVolume()
// static
void LLVOVolume::initClass()
{
sObjectMediaClient = new LLObjectMediaDataClient();
sObjectMediaNavigateClient = new LLObjectMediaNavigateClient();
// gSavedSettings better be around
const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries);
sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries);
}
// static
@ -1634,7 +1689,7 @@ bool LLVOVolume::hasMedia() const
void LLVOVolume::requestMediaDataUpdate()
{
sObjectMediaClient->fetchMedia(this);
sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this));
}
void LLVOVolume::cleanUpMediaImpls()
@ -1834,7 +1889,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
llinfos << "broadcasting navigate with URI " << new_location << llendl;
sObjectMediaNavigateClient->navigate(this, face_index, new_location);
sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location);
}
}
break;
@ -1860,7 +1915,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
void LLVOVolume::sendMediaDataUpdate()
{
sObjectMediaClient->updateMedia(this);
sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this));
}
void LLVOVolume::removeMediaImpl(S32 texture_index)
@ -1953,6 +2008,22 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const
return NULL;
}
F64 LLVOVolume::getTotalMediaInterest() const
{
F64 interest = (F64)0.0;
int i = 0;
const int end = getNumTEs();
for ( ; i < end; ++i)
{
const viewer_media_t &impl = getMediaImpl(i);
if (!impl.isNull())
{
interest += impl->getInterest();
}
}
return interest;
}
S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id)
{
S32 end = (S32)mMediaImplList.size() ;

View File

@ -255,6 +255,7 @@ public:
viewer_media_t getMediaImpl(U8 face_id) const;
S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id);
F64 getTotalMediaInterest() const;
bool hasMedia() const;

View File

@ -0,0 +1,481 @@
/**
* @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 <iostream>
#include "../test/lltut.h"
#include "llsdserialize.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"
#include <boost/lexical_cast.hpp>
#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,DIST,INT,URL1,URL2) " \
<llsd> \
<map> \
<key>uuid</key> \
<string>" ID "</string> \
<key>distance</key> \
<real>" DIST "</real> \
<key>interest</key> \
<real>" INT "</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 " \
</map> \
</llsd>"
#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)
const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0");
#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;
// 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:
if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 )
{
responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason");
}
else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)
{
LLSD result;
LLSD error;
error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE;
result["error"] = error;
responder->result(result);
}
else {
responder->result(LLSD());
}
}
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)
{ mRep["media_data"] = media_data_array; }
virtual F64 getDistanceFromAvatar() const
{ return (LLSD::Real)mRep["distance"]; }
virtual F64 getTotalMediaInterest() const
{ return (LLSD::Real)mRep["interest"]; }
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mRep["cap_urls"][name]; }
int getNumBounceBacks() const
{ return mNumBounceBacks; }
private:
LLSD mRep;
int mNumBounceBacks;
};
namespace tut
{
struct mediadataclient
{
mediadataclient() {
gPostRecords = &mLLSD;
// 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();
{
// queue time w/ no delay ensures that LLEventTimer::updateClass() will hit the tick()
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);
mdc->fetchMedia(o);
// Make sure no posts happened yet...
ensure("post records", gPostRecords->size(), 0);
LLEventTimer::updateClass();
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 LLEventTimer::updateClass() will hit the tick()
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);
mdc->updateMedia(o);
ensure("post records", gPostRecords->size(), 0);
LLEventTimer::updateClass();
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(0,0,4);
const char *TEST_URL = "http://example.com";
mdc->navigate(o, 0, TEST_URL);
ensure("post records", gPostRecords->size(), 0);
LLEventTimer::updateClass();
// 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,"3.0","1.0"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_2,"1.0","1.0"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(
_DATA(VALID_OBJECT_ID_3,"2.0","1.0"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);
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...
LLEventTimer::updateClass();
ensure("post records", gPostRecords->size(), 1);
LLEventTimer::updateClass();
ensure("post records", gPostRecords->size(), 2);
LLEventTimer::updateClass();
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",
"1.0",
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(0,0,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)
{
LLEventTimer::updateClass();
ensure("post records " + STR(i), gPostRecords->size(), i+1);
LLEventTimer::updateClass();
}
// Do some extre pumps to make sure no other timer work occurs.
LLEventTimer::updateClass();
LLEventTimer::updateClass();
LLEventTimer::updateClass();
// 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",
"1.0",
FAKE_OBJECT_MEDIA_CAP_URL,
FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR));
{
LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(0,0,4);
const char *TEST_URL = "http://example.com";
mdc->navigate(o, 0, TEST_URL);
ensure("post records", gPostRecords->size(), 0);
LLEventTimer::updateClass();
// 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);
}
}