PageRenderTime 31ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

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