PageRenderTime 128ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/test/llevents_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 783 lines | 560 code | 32 blank | 191 comment | 6 complexity | 76ba2881e61d2c040a30b9221f486f30 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llevents_tut.cpp
  3. * @author Nat Goodspeed
  4. * @date 2008-09-12
  5. * @brief Test of llevents.h
  6. *
  7. * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #if LL_WINDOWS
  29. #pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
  30. #endif
  31. // Precompiled header
  32. #include "linden_common.h"
  33. // associated header
  34. // UGLY HACK! We want to verify state internal to the classes without
  35. // providing public accessors.
  36. #define testable public
  37. #include "llevents.h"
  38. #undef testable
  39. #include "lllistenerwrapper.h"
  40. // STL headers
  41. // std headers
  42. #include <iostream>
  43. #include <typeinfo>
  44. // external library headers
  45. #include <boost/bind.hpp>
  46. #include <boost/shared_ptr.hpp>
  47. #include <boost/assign/list_of.hpp>
  48. // other Linden headers
  49. #include "lltut.h"
  50. #include "stringize.h"
  51. #include "tests/listener.h"
  52. using boost::assign::list_of;
  53. #ifdef LL_LINUX
  54. #define CATCH_MISSED_LINUX_EXCEPTION(exception, threw) \
  55. catch (const std::runtime_error& ex) \
  56. { \
  57. /* This clause is needed on Linux, on the viewer side, because the */ \
  58. /* exception isn't caught by the clause above. Warn the user... */ \
  59. std::cerr << "Failed to catch " << typeid(ex).name() << std::endl; \
  60. /* But if the expected exception was thrown, allow the test to */ \
  61. /* succeed anyway. Not sure how else to handle this odd case. */ \
  62. /* This approach is also used in llsdmessage_test.cpp. */ \
  63. if (std::string(typeid(ex).name()) == typeid(exception).name()) \
  64. { \
  65. threw = ex.what(); \
  66. /*std::cout << ex.what() << std::endl;*/ \
  67. } \
  68. else \
  69. { \
  70. /* We don't even recognize this exception. Let it propagate */ \
  71. /* out to TUT to fail the test. */ \
  72. throw; \
  73. } \
  74. } \
  75. catch (...) \
  76. { \
  77. std::cerr << "Utterly failed to catch expected exception " << #exception << "!" << \
  78. std::endl; \
  79. /* This indicates a problem in the test that should be addressed. */ \
  80. throw; \
  81. }
  82. #else // LL_LINUX
  83. #define CATCH_MISSED_LINUX_EXCEPTION(exception, threw) \
  84. /* Not needed on other platforms */
  85. #endif // LL_LINUX
  86. template<typename T>
  87. T make(const T& value)
  88. {
  89. return value;
  90. }
  91. /*****************************************************************************
  92. * tut test group
  93. *****************************************************************************/
  94. namespace tut
  95. {
  96. struct events_data
  97. {
  98. events_data() :
  99. pumps(LLEventPumps::instance()),
  100. listener0("first"),
  101. listener1("second")
  102. {
  103. }
  104. LLEventPumps& pumps;
  105. Listener listener0;
  106. Listener listener1;
  107. void check_listener(const std::string& desc, const Listener& listener, LLSD::Integer got)
  108. {
  109. ensure_equals(STRINGIZE(listener << ' ' << desc),
  110. listener.getLastEvent().asInteger(), got);
  111. }
  112. };
  113. typedef test_group<events_data> events_group;
  114. typedef events_group::object events_object;
  115. tut::events_group evgr("events");
  116. template<> template<>
  117. void events_object::test<1>()
  118. {
  119. set_test_name("basic operations");
  120. // Now there's a static constructor in llevents.cpp that registers on
  121. // the "mainloop" pump to call LLEventPumps::flush().
  122. // Actually -- having to modify this to track the statically-
  123. // constructed pumps in other TUT modules in this giant monolithic test
  124. // executable isn't such a hot idea.
  125. // ensure_equals("initial pump", pumps.mPumpMap.size(), 1);
  126. size_t initial_pumps(pumps.mPumpMap.size());
  127. LLEventPump& per_frame(pumps.obtain("per-frame"));
  128. ensure_equals("first explicit pump", pumps.mPumpMap.size(), initial_pumps + 1);
  129. // Verify that per_frame was instantiated as an LLEventStream.
  130. ensure("LLEventStream leaf class", dynamic_cast<LLEventStream*> (&per_frame));
  131. ensure("enabled", per_frame.enabled());
  132. // Trivial test, but posting an event to an EventPump with no
  133. // listeners should not blow up. The test is relevant because defining
  134. // a boost::signal with a non-void return signature, using the default
  135. // combiner, blows up if there are no listeners. This is because the
  136. // default combiner is defined to return the value returned by the
  137. // last listener, which is meaningless if there were no listeners.
  138. per_frame.post(0);
  139. LLBoundListener connection = listener0.listenTo(per_frame);
  140. ensure("connected", connection.connected());
  141. ensure("not blocked", !connection.blocked());
  142. per_frame.post(1);
  143. check_listener("received", listener0, 1);
  144. { // block the connection
  145. LLEventPump::Blocker block(connection);
  146. ensure("blocked", connection.blocked());
  147. per_frame.post(2);
  148. check_listener("not updated", listener0, 1);
  149. } // unblock
  150. ensure("unblocked", !connection.blocked());
  151. per_frame.post(3);
  152. check_listener("unblocked", listener0, 3);
  153. LLBoundListener sameConnection = per_frame.getListener(listener0.getName());
  154. ensure("still connected", sameConnection.connected());
  155. ensure("still not blocked", !sameConnection.blocked());
  156. { // block it again
  157. LLEventPump::Blocker block(sameConnection);
  158. ensure("re-blocked", sameConnection.blocked());
  159. per_frame.post(4);
  160. check_listener("re-blocked", listener0, 3);
  161. } // unblock
  162. std::string threw;
  163. try
  164. {
  165. // NOTE: boost::bind() saves its arguments by VALUE! If you pass
  166. // an object instance rather than a pointer, you'll end up binding
  167. // to an internal copy of that instance! Use boost::ref() to
  168. // capture a reference instead.
  169. per_frame.listen(listener0.getName(), // note bug, dup name
  170. boost::bind(&Listener::call, boost::ref(listener1), _1));
  171. }
  172. catch (const LLEventPump::DupListenerName& e)
  173. {
  174. threw = e.what();
  175. }
  176. CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::DupListenerName, threw)
  177. ensure_equals(threw,
  178. std::string("DupListenerName: "
  179. "Attempt to register duplicate listener name '") +
  180. listener0.getName() + "' on " + typeid(per_frame).name() +
  181. " '" + per_frame.getName() + "'");
  182. // do it right this time
  183. listener1.listenTo(per_frame);
  184. per_frame.post(5);
  185. check_listener("got", listener0, 5);
  186. check_listener("got", listener1, 5);
  187. per_frame.enable(false);
  188. per_frame.post(6);
  189. check_listener("didn't get", listener0, 5);
  190. check_listener("didn't get", listener1, 5);
  191. per_frame.enable();
  192. per_frame.post(7);
  193. check_listener("got", listener0, 7);
  194. check_listener("got", listener1, 7);
  195. per_frame.stopListening(listener0.getName());
  196. ensure("disconnected 0", ! connection.connected());
  197. ensure("disconnected 1", ! sameConnection.connected());
  198. per_frame.post(8);
  199. check_listener("disconnected", listener0, 7);
  200. check_listener("still connected", listener1, 8);
  201. per_frame.stopListening(listener1.getName());
  202. per_frame.post(9);
  203. check_listener("disconnected", listener1, 8);
  204. }
  205. template<> template<>
  206. void events_object::test<2>()
  207. {
  208. set_test_name("callstop() returning true");
  209. LLEventPump& per_frame(pumps.obtain("per-frame"));
  210. listener0.reset(0);
  211. listener1.reset(0);
  212. LLBoundListener bound0 = listener0.listenTo(per_frame, &Listener::callstop);
  213. LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call,
  214. // after listener0
  215. make<LLEventPump::NameList>(list_of(listener0.getName())));
  216. ensure("enabled", per_frame.enabled());
  217. ensure("connected 0", bound0.connected());
  218. ensure("unblocked 0", !bound0.blocked());
  219. ensure("connected 1", bound1.connected());
  220. ensure("unblocked 1", !bound1.blocked());
  221. per_frame.post(1);
  222. check_listener("got", listener0, 1);
  223. // Because listener0.callstop() returns true, control never reaches listener1.call().
  224. check_listener("got", listener1, 0);
  225. }
  226. bool chainEvents(Listener& someListener, const LLSD& event)
  227. {
  228. // Make this call so we can watch for side effects for test purposes.
  229. someListener.call(event);
  230. // This function represents a recursive event chain -- or some other
  231. // scenario in which an event handler raises additional events.
  232. int value = event.asInteger();
  233. if (value)
  234. {
  235. LLEventPumps::instance().obtain("login").post(value - 1);
  236. }
  237. return false;
  238. }
  239. template<> template<>
  240. void events_object::test<3>()
  241. {
  242. set_test_name("LLEventQueue delayed action");
  243. // This access is NOT legal usage: we can do it only because we're
  244. // hacking private for test purposes. Normally we'd either compile in
  245. // a particular name, or (later) edit a config file.
  246. pumps.mQueueNames.insert("login");
  247. LLEventPump& login(pumps.obtain("login"));
  248. // The "mainloop" pump is special: posting on that implicitly calls
  249. // LLEventPumps::flush(), which in turn should flush our "login"
  250. // LLEventQueue.
  251. LLEventPump& mainloop(pumps.obtain("mainloop"));
  252. ensure("LLEventQueue leaf class", dynamic_cast<LLEventQueue*> (&login));
  253. listener0.listenTo(login);
  254. listener0.reset(0);
  255. login.post(1);
  256. check_listener("waiting for queued event", listener0, 0);
  257. mainloop.post(LLSD());
  258. check_listener("got queued event", listener0, 1);
  259. login.stopListening(listener0.getName());
  260. // Verify that when an event handler posts a new event on the same
  261. // LLEventQueue, it doesn't get processed in the same flush() call --
  262. // it waits until the next flush() call.
  263. listener0.reset(17);
  264. login.listen("chainEvents", boost::bind(chainEvents, boost::ref(listener0), _1));
  265. login.post(1);
  266. check_listener("chainEvents(1) not yet called", listener0, 17);
  267. mainloop.post(LLSD());
  268. check_listener("chainEvents(1) called", listener0, 1);
  269. mainloop.post(LLSD());
  270. check_listener("chainEvents(0) called", listener0, 0);
  271. mainloop.post(LLSD());
  272. check_listener("chainEvents(-1) not called", listener0, 0);
  273. login.stopListening("chainEvents");
  274. }
  275. template<> template<>
  276. void events_object::test<4>()
  277. {
  278. set_test_name("explicitly-instantiated LLEventStream");
  279. // Explicitly instantiate an LLEventStream, and verify that it
  280. // self-registers with LLEventPumps
  281. size_t registered = pumps.mPumpMap.size();
  282. size_t owned = pumps.mOurPumps.size();
  283. LLEventPump* localInstance;
  284. {
  285. LLEventStream myEventStream("stream");
  286. localInstance = &myEventStream;
  287. LLEventPump& stream(pumps.obtain("stream"));
  288. ensure("found named LLEventStream instance", &stream == localInstance);
  289. ensure_equals("registered new instance", pumps.mPumpMap.size(), registered + 1);
  290. ensure_equals("explicit instance not owned", pumps.mOurPumps.size(), owned);
  291. } // destroy myEventStream -- should unregister
  292. ensure_equals("destroyed instance unregistered", pumps.mPumpMap.size(), registered);
  293. ensure_equals("destroyed instance not owned", pumps.mOurPumps.size(), owned);
  294. LLEventPump& stream(pumps.obtain("stream"));
  295. ensure("new LLEventStream instance", &stream != localInstance);
  296. ensure_equals("obtain()ed instance registered", pumps.mPumpMap.size(), registered + 1);
  297. ensure_equals("obtain()ed instance owned", pumps.mOurPumps.size(), owned + 1);
  298. }
  299. template<> template<>
  300. void events_object::test<5>()
  301. {
  302. set_test_name("stopListening()");
  303. LLEventPump& login(pumps.obtain("login"));
  304. listener0.listenTo(login);
  305. login.stopListening(listener0.getName());
  306. // should not throw because stopListening() should have removed name
  307. listener0.listenTo(login, &Listener::callstop);
  308. LLBoundListener wrong = login.getListener("bogus");
  309. ensure("bogus connection disconnected", !wrong.connected());
  310. ensure("bogus connection blocked", wrong.blocked());
  311. }
  312. template<> template<>
  313. void events_object::test<6>()
  314. {
  315. set_test_name("chaining LLEventPump instances");
  316. LLEventPump& upstream(pumps.obtain("upstream"));
  317. // One potentially-useful construct is to chain LLEventPumps together.
  318. // Among other things, this allows you to turn subsets of listeners on
  319. // and off in groups.
  320. LLEventPump& filter0(pumps.obtain("filter0"));
  321. LLEventPump& filter1(pumps.obtain("filter1"));
  322. upstream.listen(filter0.getName(), boost::bind(&LLEventPump::post, boost::ref(filter0), _1));
  323. upstream.listen(filter1.getName(), boost::bind(&LLEventPump::post, boost::ref(filter1), _1));
  324. listener0.listenTo(filter0);
  325. listener1.listenTo(filter1);
  326. listener0.reset(0);
  327. listener1.reset(0);
  328. upstream.post(1);
  329. check_listener("got unfiltered", listener0, 1);
  330. check_listener("got unfiltered", listener1, 1);
  331. filter0.enable(false);
  332. upstream.post(2);
  333. check_listener("didn't get filtered", listener0, 1);
  334. check_listener("got filtered", listener1, 2);
  335. }
  336. template<> template<>
  337. void events_object::test<7>()
  338. {
  339. set_test_name("listener dependency order");
  340. typedef LLEventPump::NameList NameList;
  341. typedef Collect::StringList StringList;
  342. LLEventPump& button(pumps.obtain("button"));
  343. Collect collector;
  344. button.listen("Mary",
  345. boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
  346. // state that "Mary" must come after "checked"
  347. make<NameList> (list_of("checked")));
  348. button.listen("checked",
  349. boost::bind(&Collect::add, boost::ref(collector), "checked", _1),
  350. // "checked" must come after "spot"
  351. make<NameList> (list_of("spot")));
  352. button.listen("spot",
  353. boost::bind(&Collect::add, boost::ref(collector), "spot", _1));
  354. button.post(1);
  355. ensure_equals(collector.result, make<StringList>(list_of("spot")("checked")("Mary")));
  356. collector.clear();
  357. button.stopListening("Mary");
  358. button.listen("Mary",
  359. boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
  360. LLEventPump::empty, // no after dependencies
  361. // now "Mary" must come before "spot"
  362. make<NameList>(list_of("spot")));
  363. button.post(2);
  364. ensure_equals(collector.result, make<StringList>(list_of("Mary")("spot")("checked")));
  365. collector.clear();
  366. button.stopListening("spot");
  367. std::string threw;
  368. try
  369. {
  370. button.listen("spot",
  371. boost::bind(&Collect::add, boost::ref(collector), "spot", _1),
  372. // after "Mary" and "checked" -- whoops!
  373. make<NameList>(list_of("Mary")("checked")));
  374. }
  375. catch (const LLEventPump::Cycle& e)
  376. {
  377. threw = e.what();
  378. // std::cout << "Caught: " << e.what() << '\n';
  379. }
  380. CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::Cycle, threw)
  381. // Obviously the specific wording of the exception text can
  382. // change; go ahead and change the test to match.
  383. // Establish that it contains:
  384. // - the name and runtime type of the LLEventPump
  385. ensure_contains("LLEventPump type", threw, typeid(button).name());
  386. ensure_contains("LLEventPump name", threw, "'button'");
  387. // - the name of the new listener that caused the problem
  388. ensure_contains("new listener name", threw, "'spot'");
  389. // - a synopsis of the problematic dependencies.
  390. ensure_contains("cyclic dependencies", threw,
  391. "\"Mary\" -> before (\"spot\")");
  392. ensure_contains("cyclic dependencies", threw,
  393. "after (\"spot\") -> \"checked\"");
  394. ensure_contains("cyclic dependencies", threw,
  395. "after (\"Mary\", \"checked\") -> \"spot\"");
  396. button.listen("yellow",
  397. boost::bind(&Collect::add, boost::ref(collector), "yellow", _1),
  398. make<NameList>(list_of("checked")));
  399. button.listen("shoelaces",
  400. boost::bind(&Collect::add, boost::ref(collector), "shoelaces", _1),
  401. make<NameList>(list_of("checked")));
  402. button.post(3);
  403. ensure_equals(collector.result, make<StringList>(list_of("Mary")("checked")("yellow")("shoelaces")));
  404. collector.clear();
  405. threw.clear();
  406. try
  407. {
  408. button.listen("of",
  409. boost::bind(&Collect::add, boost::ref(collector), "of", _1),
  410. make<NameList>(list_of("shoelaces")),
  411. make<NameList>(list_of("yellow")));
  412. }
  413. catch (const LLEventPump::OrderChange& e)
  414. {
  415. threw = e.what();
  416. // std::cout << "Caught: " << e.what() << '\n';
  417. }
  418. CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::OrderChange, threw)
  419. // Same remarks about the specific wording of the exception. Just
  420. // ensure that it contains enough information to clarify the
  421. // problem and what must be done to resolve it.
  422. ensure_contains("LLEventPump type", threw, typeid(button).name());
  423. ensure_contains("LLEventPump name", threw, "'button'");
  424. ensure_contains("new listener name", threw, "'of'");
  425. ensure_contains("prev listener name", threw, "'yellow'");
  426. // std::cout << "Thrown Exception: " << threw << std::endl;
  427. ensure_contains("old order", threw, "was: Mary, checked, yellow, shoelaces");
  428. ensure_contains("new order", threw, "now: Mary, checked, shoelaces, of, yellow");
  429. button.post(4);
  430. ensure_equals(collector.result, make<StringList>(list_of("Mary")("checked")("yellow")("shoelaces")));
  431. }
  432. template<> template<>
  433. void events_object::test<8>()
  434. {
  435. set_test_name("tweaked and untweaked LLEventPump instance names");
  436. { // nested scope
  437. // Hand-instantiate an LLEventStream...
  438. LLEventStream bob("bob");
  439. std::string threw;
  440. try
  441. {
  442. // then another with a duplicate name.
  443. LLEventStream bob2("bob");
  444. }
  445. catch (const LLEventPump::DupPumpName& e)
  446. {
  447. threw = e.what();
  448. // std::cout << "Caught: " << e.what() << '\n';
  449. }
  450. CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::DupPumpName, threw)
  451. ensure("Caught DupPumpName", !threw.empty());
  452. } // delete first 'bob'
  453. LLEventStream bob("bob"); // should work, previous one unregistered
  454. LLEventStream bob1("bob", true);// allowed to tweak name
  455. ensure_equals("tweaked LLEventStream name", bob1.getName(), "bob1");
  456. std::vector<boost::shared_ptr<LLEventStream> > streams;
  457. for (int i = 2; i <= 10; ++i)
  458. {
  459. streams.push_back(boost::shared_ptr<LLEventStream>(new LLEventStream("bob", true)));
  460. }
  461. ensure_equals("last tweaked LLEventStream name", streams.back()->getName(), "bob10");
  462. }
  463. // Define a function that accepts an LLListenerOrPumpName
  464. void eventSource(const LLListenerOrPumpName& listener)
  465. {
  466. // Pretend that some time has elapsed. Call listener immediately.
  467. listener(17);
  468. }
  469. template<> template<>
  470. void events_object::test<9>()
  471. {
  472. set_test_name("LLListenerOrPumpName");
  473. // Passing a boost::bind() expression to LLListenerOrPumpName
  474. listener0.reset(0);
  475. eventSource(boost::bind(&Listener::call, boost::ref(listener0), _1));
  476. check_listener("got by listener", listener0, 17);
  477. // Passing a string LLEventPump name to LLListenerOrPumpName
  478. listener0.reset(0);
  479. LLEventStream random("random");
  480. listener0.listenTo(random);
  481. eventSource("random");
  482. check_listener("got by pump name", listener0, 17);
  483. std::string threw;
  484. try
  485. {
  486. LLListenerOrPumpName empty;
  487. empty(17);
  488. }
  489. catch (const LLListenerOrPumpName::Empty& e)
  490. {
  491. threw = e.what();
  492. }
  493. CATCH_MISSED_LINUX_EXCEPTION(LLListenerOrPumpName::Empty, threw)
  494. ensure("threw Empty", !threw.empty());
  495. }
  496. class TempListener: public Listener
  497. {
  498. public:
  499. TempListener(const std::string& name, bool& liveFlag) :
  500. Listener(name), mLiveFlag(liveFlag)
  501. {
  502. mLiveFlag = true;
  503. }
  504. virtual ~TempListener()
  505. {
  506. mLiveFlag = false;
  507. }
  508. private:
  509. bool& mLiveFlag;
  510. };
  511. template<> template<>
  512. void events_object::test<10>()
  513. {
  514. set_test_name("listen(boost::bind(...TempListener...))");
  515. // listen() can't do anything about a plain TempListener instance:
  516. // it's not managed with shared_ptr, nor is it an LLEventTrackable subclass
  517. bool live = false;
  518. LLEventPump& heaptest(pumps.obtain("heaptest"));
  519. LLBoundListener connection;
  520. {
  521. TempListener tempListener("temp", live);
  522. ensure("TempListener constructed", live);
  523. connection = heaptest.listen(tempListener.getName(),
  524. boost::bind(&Listener::call,
  525. boost::ref(tempListener),
  526. _1));
  527. heaptest.post(1);
  528. check_listener("received", tempListener, 1);
  529. } // presumably this will make newListener go away?
  530. // verify that
  531. ensure("TempListener destroyed", !live);
  532. // This is the case against which we can't defend. Don't even try to
  533. // post to heaptest -- that would engage Undefined Behavior.
  534. // Cautiously inspect connection...
  535. ensure("misleadingly connected", connection.connected());
  536. // then disconnect by hand.
  537. heaptest.stopListening("temp");
  538. }
  539. template<> template<>
  540. void events_object::test<11>()
  541. {
  542. set_test_name("listen(boost::bind(...weak_ptr...))");
  543. // listen() detecting weak_ptr<TempListener> in boost::bind() object
  544. bool live = false;
  545. LLEventPump& heaptest(pumps.obtain("heaptest"));
  546. LLBoundListener connection;
  547. ensure("default state", !connection.connected());
  548. {
  549. boost::shared_ptr<TempListener> newListener(new TempListener("heap", live));
  550. newListener->reset();
  551. ensure("TempListener constructed", live);
  552. connection = heaptest.listen(newListener->getName(),
  553. boost::bind(&Listener::call,
  554. weaken(newListener),
  555. _1));
  556. ensure("new connection", connection.connected());
  557. heaptest.post(1);
  558. check_listener("received", *newListener, 1);
  559. } // presumably this will make newListener go away?
  560. // verify that
  561. ensure("TempListener destroyed", !live);
  562. ensure("implicit disconnect", !connection.connected());
  563. // now just make sure we don't blow up trying to access a freed object!
  564. heaptest.post(2);
  565. }
  566. template<> template<>
  567. void events_object::test<12>()
  568. {
  569. set_test_name("listen(boost::bind(...shared_ptr...))");
  570. /*==========================================================================*|
  571. // DISABLED because I've made this case produce a compile error.
  572. // Following the error leads the disappointed dev to a comment
  573. // instructing her to use the weaken() function to bind a weak_ptr<T>
  574. // instead of binding a shared_ptr<T>, and explaining why. I know of
  575. // no way to use TUT to code a repeatable test in which the expected
  576. // outcome is a compile error. The interested reader is invited to
  577. // uncomment this block and build to see for herself.
  578. // listen() detecting shared_ptr<TempListener> in boost::bind() object
  579. bool live = false;
  580. LLEventPump& heaptest(pumps.obtain("heaptest"));
  581. LLBoundListener connection;
  582. std::string listenerName("heap");
  583. ensure("default state", !connection.connected());
  584. {
  585. boost::shared_ptr<TempListener> newListener(new TempListener(listenerName, live));
  586. ensure_equals("use_count", newListener.use_count(), 1);
  587. newListener->reset();
  588. ensure("TempListener constructed", live);
  589. connection = heaptest.listen(newListener->getName(),
  590. boost::bind(&Listener::call, newListener, _1));
  591. ensure("new connection", connection.connected());
  592. ensure_equals("use_count", newListener.use_count(), 2);
  593. heaptest.post(1);
  594. check_listener("received", *newListener, 1);
  595. } // this should make newListener go away...
  596. // Unfortunately, the fact that we've bound a shared_ptr by value into
  597. // our LLEventPump means that copy will keep the referenced object alive.
  598. ensure("TempListener still alive", live);
  599. ensure("still connected", connection.connected());
  600. // disconnecting explicitly should delete the TempListener...
  601. heaptest.stopListening(listenerName);
  602. #if 0 // however, in my experience, it does not. I don't know why not.
  603. // Ah: on 2009-02-19, Frank Mori Hess, author of the Boost.Signals2
  604. // library, stated on the boost-users mailing list:
  605. // http://www.nabble.com/Re%3A--signals2--review--The-review-of-the-signals2-library-(formerly-thread_safe_signals)-begins-today%2C-Nov-1st-p22102367.html
  606. // "It will get destroyed eventually. The signal cleans up its slot
  607. // list little by little during connect/invoke. It doesn't immediately
  608. // remove disconnected slots from the slot list since other threads
  609. // might be using the same slot list concurrently. It might be
  610. // possible to make it immediately reset the shared_ptr owning the
  611. // slot though, leaving an empty shared_ptr in the slot list, since
  612. // that wouldn't invalidate any iterators."
  613. ensure("TempListener destroyed", ! live);
  614. ensure("implicit disconnect", ! connection.connected());
  615. #endif // 0
  616. // now just make sure we don't blow up trying to access a freed object!
  617. heaptest.post(2);
  618. |*==========================================================================*/
  619. }
  620. class TempTrackableListener: public TempListener, public LLEventTrackable
  621. {
  622. public:
  623. TempTrackableListener(const std::string& name, bool& liveFlag):
  624. TempListener(name, liveFlag)
  625. {}
  626. };
  627. template<> template<>
  628. void events_object::test<13>()
  629. {
  630. set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
  631. bool live = false;
  632. LLEventPump& heaptest(pumps.obtain("heaptest"));
  633. LLBoundListener connection;
  634. {
  635. TempTrackableListener tempListener("temp", live);
  636. ensure("TempTrackableListener constructed", live);
  637. connection = heaptest.listen(tempListener.getName(),
  638. boost::bind(&TempTrackableListener::call,
  639. boost::ref(tempListener), _1));
  640. heaptest.post(1);
  641. check_listener("received", tempListener, 1);
  642. } // presumably this will make tempListener go away?
  643. // verify that
  644. ensure("TempTrackableListener destroyed", ! live);
  645. ensure("implicit disconnect", ! connection.connected());
  646. // now just make sure we don't blow up trying to access a freed object!
  647. heaptest.post(2);
  648. }
  649. template<> template<>
  650. void events_object::test<14>()
  651. {
  652. set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
  653. bool live = false;
  654. LLEventPump& heaptest(pumps.obtain("heaptest"));
  655. LLBoundListener connection;
  656. {
  657. TempTrackableListener* newListener(new TempTrackableListener("temp", live));
  658. ensure("TempTrackableListener constructed", live);
  659. connection = heaptest.listen(newListener->getName(),
  660. boost::bind(&TempTrackableListener::call,
  661. newListener, _1));
  662. heaptest.post(1);
  663. check_listener("received", *newListener, 1);
  664. // explicitly destroy newListener
  665. delete newListener;
  666. }
  667. // verify that
  668. ensure("TempTrackableListener destroyed", ! live);
  669. ensure("implicit disconnect", ! connection.connected());
  670. // now just make sure we don't blow up trying to access a freed object!
  671. heaptest.post(2);
  672. }
  673. template<> template<>
  674. void events_object::test<15>()
  675. {
  676. // This test ensures that using an LLListenerWrapper subclass doesn't
  677. // block Boost.Signals2 from recognizing a bound LLEventTrackable
  678. // subclass.
  679. set_test_name("listen(llwrap<LLLogListener>(boost::bind(...TempTrackableListener ref...)))");
  680. bool live = false;
  681. LLEventPump& heaptest(pumps.obtain("heaptest"));
  682. LLBoundListener connection;
  683. {
  684. TempTrackableListener tempListener("temp", live);
  685. ensure("TempTrackableListener constructed", live);
  686. connection = heaptest.listen(tempListener.getName(),
  687. llwrap<LLLogListener>(
  688. boost::bind(&TempTrackableListener::call,
  689. boost::ref(tempListener), _1)));
  690. heaptest.post(1);
  691. check_listener("received", tempListener, 1);
  692. } // presumably this will make tempListener go away?
  693. // verify that
  694. ensure("TempTrackableListener destroyed", ! live);
  695. ensure("implicit disconnect", ! connection.connected());
  696. // now just make sure we don't blow up trying to access a freed object!
  697. heaptest.post(2);
  698. }
  699. class TempSharedListener: public TempListener,
  700. public boost::enable_shared_from_this<TempSharedListener>
  701. {
  702. public:
  703. TempSharedListener(const std::string& name, bool& liveFlag):
  704. TempListener(name, liveFlag)
  705. {}
  706. };
  707. template<> template<>
  708. void events_object::test<16>()
  709. {
  710. set_test_name("listen(boost::bind(...TempSharedListener ref...))");
  711. #if 0
  712. bool live = false;
  713. LLEventPump& heaptest(pumps.obtain("heaptest"));
  714. LLBoundListener connection;
  715. {
  716. // We MUST have at least one shared_ptr to an
  717. // enable_shared_from_this subclass object before
  718. // shared_from_this() can work.
  719. boost::shared_ptr<TempSharedListener>
  720. tempListener(new TempSharedListener("temp", live));
  721. ensure("TempSharedListener constructed", live);
  722. // However, we're not passing either the shared_ptr or its
  723. // corresponding weak_ptr -- instead, we're passing a reference to
  724. // the TempSharedListener.
  725. /*==========================================================================*|
  726. std::cout << "Capturing const ref" << std::endl;
  727. const boost::enable_shared_from_this<TempSharedListener>& cref(*tempListener);
  728. std::cout << "Capturing const ptr" << std::endl;
  729. const boost::enable_shared_from_this<TempSharedListener>* cp(&cref);
  730. std::cout << "Capturing non-const ptr" << std::endl;
  731. boost::enable_shared_from_this<TempSharedListener>* p(const_cast<boost::enable_shared_from_this<TempSharedListener>*>(cp));
  732. std::cout << "Capturing shared_from_this()" << std::endl;
  733. boost::shared_ptr<TempSharedListener> sp(p->shared_from_this());
  734. std::cout << "Capturing weak_ptr" << std::endl;
  735. boost::weak_ptr<TempSharedListener> wp(weaken(sp));
  736. std::cout << "Binding weak_ptr" << std::endl;
  737. |*==========================================================================*/
  738. connection = heaptest.listen(tempListener->getName(),
  739. boost::bind(&TempSharedListener::call, *tempListener, _1));
  740. heaptest.post(1);
  741. check_listener("received", *tempListener, 1);
  742. } // presumably this will make tempListener go away?
  743. // verify that
  744. ensure("TempSharedListener destroyed", ! live);
  745. ensure("implicit disconnect", ! connection.connected());
  746. // now just make sure we don't blow up trying to access a freed object!
  747. heaptest.post(2);
  748. #endif // 0
  749. }
  750. } // namespace tut