llevents_tut.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:33k
源码类别:

游戏引擎

开发平台:

C++ Builder

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