Imported existing code
This commit is contained in:
260
indra/llimage/tests/llimageworker_test.cpp
Normal file
260
indra/llimage/tests/llimageworker_test.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @file llimageworker_test.cpp
|
||||
* @author Merov Linden
|
||||
* @date 2009-04-28
|
||||
*
|
||||
* $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 <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
// Class to test
|
||||
#include "../llimageworker.h"
|
||||
// For timer class
|
||||
#include "../llcommon/lltimer.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.
|
||||
|
||||
LLImageBase::LLImageBase() {}
|
||||
LLImageBase::~LLImageBase() {}
|
||||
void LLImageBase::dump() { }
|
||||
void LLImageBase::sanityCheck() { }
|
||||
void LLImageBase::deleteData() { }
|
||||
U8* LLImageBase::allocateData(S32 size) { return NULL; }
|
||||
U8* LLImageBase::reallocateData(S32 size) { return NULL; }
|
||||
|
||||
LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) { }
|
||||
LLImageRaw::~LLImageRaw() { }
|
||||
void LLImageRaw::deleteData() { }
|
||||
U8* LLImageRaw::allocateData(S32 size) { return NULL; }
|
||||
U8* LLImageRaw::reallocateData(S32 size) { return NULL; }
|
||||
|
||||
// End Stubbing
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// TUT
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
namespace tut
|
||||
{
|
||||
// Test wrapper declarations
|
||||
|
||||
// Note: We derive the responder class for 2 reasons:
|
||||
// 1. It's a pure virtual class and we can't compile without completed() being implemented
|
||||
// 2. We actually need a responder to test that the thread work test completed
|
||||
// We implement this making no assumption on what's done in the thread or worker
|
||||
// though, just that the responder's completed() method is called in the end.
|
||||
// Note on responders: responders are ref counted and *will* be deleted by the request they are
|
||||
// attached to when the queued request is deleted. The recommended way of using them is to
|
||||
// create them when creating a request, put a callback method in completed() and not rely on
|
||||
// anything to survive in the responder object once completed() has been called. Let the request
|
||||
// do the deletion and clean up itself.
|
||||
class responder_test : public LLImageDecodeThread::Responder
|
||||
{
|
||||
public:
|
||||
responder_test(bool* res)
|
||||
{
|
||||
done = res;
|
||||
*done = false;
|
||||
}
|
||||
virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
|
||||
{
|
||||
*done = true;
|
||||
}
|
||||
private:
|
||||
// This is what can be thought of as the minimal implementation of a responder
|
||||
// Done will be switched to true when completed() is called and can be tested
|
||||
// outside the responder. A better way of doing this is to store a callback here.
|
||||
bool* done;
|
||||
};
|
||||
|
||||
// Test wrapper declaration : decode thread
|
||||
struct imagedecodethread_test
|
||||
{
|
||||
// Instance to be tested
|
||||
LLImageDecodeThread* mThread;
|
||||
|
||||
// Constructor and destructor of the test wrapper
|
||||
imagedecodethread_test()
|
||||
{
|
||||
mThread = NULL;
|
||||
}
|
||||
~imagedecodethread_test()
|
||||
{
|
||||
delete mThread;
|
||||
}
|
||||
};
|
||||
|
||||
// Test wrapper declaration : image worker
|
||||
// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance
|
||||
// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set.
|
||||
// Some gotcha with the destructor though (see below).
|
||||
struct imagerequest_test
|
||||
{
|
||||
// Instance to be tested
|
||||
LLImageDecodeThread::ImageRequest* mRequest;
|
||||
bool done;
|
||||
|
||||
// Constructor and destructor of the test wrapper
|
||||
imagerequest_test()
|
||||
{
|
||||
done = false;
|
||||
mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
|
||||
LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
|
||||
new responder_test(&done));
|
||||
}
|
||||
~imagerequest_test()
|
||||
{
|
||||
// We should delete the object *but*, because its destructor is protected, that cannot be
|
||||
// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine...
|
||||
//delete mRequest;
|
||||
}
|
||||
};
|
||||
|
||||
// Tut templating thingamagic: test group, object and test instance
|
||||
typedef test_group<imagedecodethread_test> imagedecodethread_t;
|
||||
typedef imagedecodethread_t::object imagedecodethread_object_t;
|
||||
tut::imagedecodethread_t tut_imagedecodethread("imagedecodethread");
|
||||
|
||||
typedef test_group<imagerequest_test> imagerequest_t;
|
||||
typedef imagerequest_t::object imagerequest_object_t;
|
||||
tut::imagerequest_t tut_imagerequest("imagerequest");
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// 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 LLImageDecodeThread interface
|
||||
// ---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Note on Unit Testing Queued Thread Classes
|
||||
//
|
||||
// Since methods on such a class are called on a separate loop and that we can't insert tut
|
||||
// ensure() calls in there, we exercise the class with 2 sets of tests:
|
||||
// - 1: Test as a single threaded instance: We declare the class but ask for no thread
|
||||
// to be spawned (easy with LLThreads since there's a boolean argument on the constructor
|
||||
// just for that). We can then unit test each public method like we do on a normal class.
|
||||
// - 2: Test as a threaded instance: We let the thread launch and check that its external
|
||||
// behavior is as expected (i.e. it runs, can accept a work order and processes
|
||||
// it). Typically though there's no guarantee that this exercises all the methods of the
|
||||
// class which is why we also need the previous "non threaded" set of unit tests for
|
||||
// complete coverage.
|
||||
//
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
template<> template<>
|
||||
void imagedecodethread_object_t::test<1>()
|
||||
{
|
||||
// Test a *non threaded* instance of the class
|
||||
mThread = new LLImageDecodeThread(false);
|
||||
ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL);
|
||||
// Test that we start with an empty list right at creation
|
||||
ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0);
|
||||
// Insert something in the queue
|
||||
bool done = false;
|
||||
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
|
||||
// Verifies we got a valid handle
|
||||
ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0);
|
||||
// Verifies that we do now have something in the queued list
|
||||
ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1);
|
||||
// Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop)
|
||||
S32 res = mThread->update(0);
|
||||
// Verifies that we successfully handled the list
|
||||
ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0);
|
||||
// Verifies that the list is now empty
|
||||
ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void imagedecodethread_object_t::test<2>()
|
||||
{
|
||||
// Test a *threaded* instance of the class
|
||||
mThread = new LLImageDecodeThread(true);
|
||||
ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL);
|
||||
// Test that we start with an empty list right at creation
|
||||
ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);
|
||||
// Insert something in the queue
|
||||
bool done = false;
|
||||
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
|
||||
// Verifies we get back a valid handle
|
||||
ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0);
|
||||
// Wait a little so to simulate the main thread doing something on its main loop...
|
||||
ms_sleep(500); // 500 milliseconds
|
||||
// Verifies that the responder has *not* been called yet in the meantime
|
||||
ensure("LLImageDecodeThread: responder creation failed", done == false);
|
||||
// Ask the thread to update: that means tells the queue to check itself and creates work requests
|
||||
mThread->update(1);
|
||||
// Wait till the thread has time to handle the work order (though it doesn't do much per work order...)
|
||||
const U32 INCREMENT_TIME = 500; // 500 milliseconds
|
||||
const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more
|
||||
U32 total_time = 0;
|
||||
while ((done == false) && (total_time < MAX_TIME))
|
||||
{
|
||||
ms_sleep(INCREMENT_TIME);
|
||||
total_time += INCREMENT_TIME;
|
||||
}
|
||||
// Verifies that the responder has now been called
|
||||
ensure("LLImageDecodeThread: threaded work unit not processed", done == true);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Test the LLImageDecodeThread::ImageRequest interface
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
template<> template<>
|
||||
void imagerequest_object_t::test<1>()
|
||||
{
|
||||
// Test that we start with a correct request at creation
|
||||
ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK());
|
||||
bool res = mRequest->processRequest();
|
||||
// Verifies that we processed the request successfully
|
||||
ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true);
|
||||
// Check that we can call the finishing call safely
|
||||
try {
|
||||
mRequest->finishRequest(false);
|
||||
} catch (...) {
|
||||
fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user