PageRenderTime 51ms CodeModel.GetById 19ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llimage/tests/llimageworker_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 262 lines | 125 code | 20 blank | 117 comment | 15 complexity | f2be33bb6ba283ba577c517f557dae2c MD5 | raw file
  1/** 
  2 * @file llimageworker_test.cpp
  3 * @author Merov Linden
  4 * @date 2009-04-28
  5 *
  6 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28// Precompiled header: almost always required for newview cpp files
 29#include "linden_common.h"
 30// Class to test 
 31#include "../llimageworker.h"
 32// For timer class
 33#include "../llcommon/lltimer.h"
 34// Tut header
 35#include "../test/lltut.h"
 36
 37// -------------------------------------------------------------------------------------------
 38// Stubbing: Declarations required to link and run the class being tested
 39// Notes: 
 40// * Add here stubbed implementation of the few classes and methods used in the class to be tested
 41// * Add as little as possible (let the link errors guide you)
 42// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
 43// * A simulator for a class can be implemented here. Please comment and document thoroughly.
 44
 45LLImageBase::LLImageBase() 
 46: mData(NULL),
 47mDataSize(0),
 48mWidth(0),
 49mHeight(0),
 50mComponents(0),
 51mBadBufferAllocation(false),
 52mAllowOverSize(false),
 53mMemType(LLMemType::MTYPE_IMAGEBASE)
 54{
 55}
 56LLImageBase::~LLImageBase() {}
 57void LLImageBase::dump() { }
 58void LLImageBase::sanityCheck() { }
 59void LLImageBase::deleteData() { }
 60U8* LLImageBase::allocateData(S32 size) { return NULL; }
 61U8* LLImageBase::reallocateData(S32 size) { return NULL; }
 62
 63LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) { }
 64LLImageRaw::~LLImageRaw() { }
 65void LLImageRaw::deleteData() { }
 66U8* LLImageRaw::allocateData(S32 size) { return NULL; }
 67U8* LLImageRaw::reallocateData(S32 size) { return NULL; }
 68
 69// End Stubbing
 70// -------------------------------------------------------------------------------------------
 71
 72// -------------------------------------------------------------------------------------------
 73// TUT
 74// -------------------------------------------------------------------------------------------
 75
 76namespace tut
 77{
 78	// Test wrapper declarations
 79
 80	// Note: We derive the responder class for 2 reasons:
 81	// 1. It's a pure virtual class and we can't compile without completed() being implemented
 82	// 2. We actually need a responder to test that the thread work test completed
 83	// We implement this making no assumption on what's done in the thread or worker
 84	// though, just that the responder's completed() method is called in the end.
 85	// Note on responders: responders are ref counted and *will* be deleted by the request they are 
 86	// attached to when the queued request is deleted. The recommended way of using them is to 
 87	// create them when creating a request, put a callback method in completed() and not rely on 
 88	// anything to survive in the responder object once completed() has been called. Let the request
 89	// do the deletion and clean up itself.
 90	class responder_test : public LLImageDecodeThread::Responder
 91	{
 92		public:
 93			responder_test(bool* res)
 94			{ 
 95				done = res;
 96				*done = false;
 97			}
 98			virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
 99			{
100				*done = true;
101			}
102		private:
103			// This is what can be thought of as the minimal implementation of a responder
104			// Done will be switched to true when completed() is called and can be tested
105			// outside the responder. A better way of doing this is to store a callback here.
106			bool* done;
107	};
108
109	// Test wrapper declaration : decode thread
110	struct imagedecodethread_test
111	{
112		// Instance to be tested
113		LLImageDecodeThread* mThread;
114
115		// Constructor and destructor of the test wrapper
116		imagedecodethread_test()
117		{
118			mThread = NULL;
119		}
120		~imagedecodethread_test()
121		{
122			delete mThread;
123		}
124	};
125
126	// Test wrapper declaration : image worker
127	// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance
128	// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set.
129	// Some gotcha with the destructor though (see below).
130	struct imagerequest_test
131	{
132		// Instance to be tested
133		LLImageDecodeThread::ImageRequest* mRequest;
134		bool done;
135
136		// Constructor and destructor of the test wrapper
137		imagerequest_test()
138		{
139			done = false;
140			mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
141											 LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
142											 new responder_test(&done));
143		}
144		~imagerequest_test()
145		{
146			// We should delete the object *but*, because its destructor is protected, that cannot be
147			// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine...
148			//delete mRequest;
149		}
150	};
151
152	// Tut templating thingamagic: test group, object and test instance
153	typedef test_group<imagedecodethread_test> imagedecodethread_t;
154	typedef imagedecodethread_t::object imagedecodethread_object_t;
155	tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread");
156
157	typedef test_group<imagerequest_test> imagerequest_t;
158	typedef imagerequest_t::object imagerequest_object_t;
159	tut::imagerequest_t tut_imagerequest("LLImageRequest");
160
161	// ---------------------------------------------------------------------------------------
162	// Test functions
163	// Notes:
164	// * Test as many as you possibly can without requiring a full blown simulation of everything
165	// * The tests are executed in sequence so the test instance state may change between calls
166	// * Remember that you cannot test private methods with tut
167	// ---------------------------------------------------------------------------------------
168
169	// ---------------------------------------------------------------------------------------
170	// Test the LLImageDecodeThread interface
171	// ---------------------------------------------------------------------------------------
172	//
173	// Note on Unit Testing Queued Thread Classes
174	//
175	// Since methods on such a class are called on a separate loop and that we can't insert tut
176	// ensure() calls in there, we exercise the class with 2 sets of tests:
177	// - 1: Test as a single threaded instance: We declare the class but ask for no thread
178	//   to be spawned (easy with LLThreads since there's a boolean argument on the constructor
179	//   just for that). We can then unit test each public method like we do on a normal class.
180	// - 2: Test as a threaded instance: We let the thread launch and check that its external 
181	//   behavior is as expected (i.e. it runs, can accept a work order and processes
182	//   it). Typically though there's no guarantee that this exercises all the methods of the
183	//   class which is why we also need the previous "non threaded" set of unit tests for
184	//   complete coverage.
185	//
186	// ---------------------------------------------------------------------------------------
187
188	template<> template<>
189	void imagedecodethread_object_t::test<1>()
190	{
191		// Test a *non threaded* instance of the class
192		mThread = new LLImageDecodeThread(false);
193		ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL);
194		// Test that we start with an empty list right at creation
195		ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0);
196		// Insert something in the queue
197		bool done = false;
198		LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
199		// Verifies we got a valid handle
200		ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0);
201		// Verifies that we do now have something in the queued list
202		ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1);
203		// Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop)
204		S32 res = mThread->update(0);
205		// Verifies that we successfully handled the list
206		ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0);
207		// Verifies that the list is now empty
208		ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0);
209	}
210
211	template<> template<>
212	void imagedecodethread_object_t::test<2>()
213	{
214		// Test a *threaded* instance of the class
215		mThread = new LLImageDecodeThread(true);
216		ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL);
217		// Test that we start with an empty list right at creation
218		ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);
219		// Insert something in the queue
220		bool done = false;
221		LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
222		// Verifies we get back a valid handle
223		ensure("LLImageDecodeThread:  threaded decodeImage(), returned handle is null", decodeHandle != 0);
224		// Wait a little so to simulate the main thread doing something on its main loop...
225		ms_sleep(500);		// 500 milliseconds
226		// Verifies that the responder has *not* been called yet in the meantime
227		ensure("LLImageDecodeThread: responder creation failed", done == false);
228		// Ask the thread to update: that means tells the queue to check itself and creates work requests
229		mThread->update(1);
230		// Wait till the thread has time to handle the work order (though it doesn't do much per work order...)
231		const U32 INCREMENT_TIME = 500;				// 500 milliseconds
232		const U32 MAX_TIME = 20 * INCREMENT_TIME;	// Do the loop 20 times max, i.e. wait 10 seconds but no more
233		U32 total_time = 0;
234		while ((done == false) && (total_time < MAX_TIME))
235		{
236			ms_sleep(INCREMENT_TIME);
237			total_time += INCREMENT_TIME;
238		}
239		// Verifies that the responder has now been called
240		ensure("LLImageDecodeThread: threaded work unit not processed", done == true);
241	}
242
243	// ---------------------------------------------------------------------------------------
244	// Test the LLImageDecodeThread::ImageRequest interface
245	// ---------------------------------------------------------------------------------------
246	
247	template<> template<>
248	void imagerequest_object_t::test<1>()
249	{
250		// Test that we start with a correct request at creation
251		ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK());
252		bool res = mRequest->processRequest();
253		// Verifies that we processed the request successfully
254		ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true);
255		// Check that we can call the finishing call safely
256		try {
257			mRequest->finishRequest(false);
258		} catch (...) {
259			fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed");
260		}
261	}
262}