diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index 328ab4ca518f51cfe5f0f42a3b32d957568c36bd..dc37066e526d1b62db9cefbdeb91f27ca522af45 100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -133,4 +133,4 @@ set(test_results ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt)
 # developers choose to disable LL_TESTS.
 if (LL_TESTS)  
     add_custom_target(tests_ok ALL DEPENDS ${test_results})
-endif (LL_TESTS)
+endif (LL_TESTS)
\ No newline at end of file
diff --git a/indra/test/llevents_tut.cpp b/indra/test/llevents_tut.cpp
index 2fb66976baf39baa63c96b2a6e0e2dfd7ceed653..57e22bbb564c7a6a442a55c3b4e8f5d799952916 100644
--- a/indra/test/llevents_tut.cpp
+++ b/indra/test/llevents_tut.cpp
@@ -25,6 +25,7 @@
  * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
  */
+
 #if LL_WINDOWS
 #pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
 #endif
@@ -37,6 +38,7 @@
 #define testable public
 #include "llevents.h"
 #undef testable
+#include "lllistenerwrapper.h"
 // STL headers
 // std headers
 #include <iostream>
@@ -48,96 +50,10 @@
 // other Linden headers
 #include "lltut.h"
 #include "stringize.h"
+#include "tests/listener.h"
 
 using boost::assign::list_of;
 
-/*****************************************************************************
-*   test listener class
-*****************************************************************************/
-class Listener;
-std::ostream& operator<<(std::ostream&, const Listener&);
-
-class Listener
-{
-public:
-    Listener(const std::string& name):
-        mName(name)
-    {
-//      std::cout << *this << ": ctor\n";
-    }
-    Listener(const Listener& that):
-        mName(that.mName),
-        mLastEvent(that.mLastEvent)
-    {
-//      std::cout << *this << ": copy\n";
-    }
-    virtual ~Listener()
-    {
-//      std::cout << *this << ": dtor\n";
-    }
-    std::string getName() const { return mName; }
-    bool call(const LLSD& event)
-    {
-//      std::cout << *this << "::call(" << event << ")\n";
-        mLastEvent = event;
-        return false;
-    }
-    bool callstop(const LLSD& event)
-    {
-//      std::cout << *this << "::callstop(" << event << ")\n";
-        mLastEvent = event;
-        return true;
-    }
-    LLSD getLastEvent() const
-    {
-//      std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n";
-        return mLastEvent;
-    }
-    void reset(const LLSD& to = LLSD())
-    {
-//      std::cout << *this << "::reset(" << to << ")\n";
-        mLastEvent = to;
-    }
-
-private:
-    std::string mName;
-    LLSD mLastEvent;
-};
-
-std::ostream& operator<<(std::ostream& out, const Listener& listener)
-{
-    out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')';
-    return out;
-}
-
-struct Collect
-{
-    bool add(const std::string& bound, const LLSD& event)
-    {
-        result.push_back(bound);
-        return false;
-    }
-    void clear() { result.clear(); }
-    typedef std::vector<std::string> StringList;
-    StringList result;
-};
-
-std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
-{
-    out << '(';
-    Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
-    if (begin != end)
-    {
-        out << '"' << *begin << '"';
-        while (++begin != end)
-        {
-            out << ", \"" << *begin << '"';
-        }
-    }
-    out << ')';
-    return out;
-}
-
 template<typename T>
 T make(const T& value) { return value; }
 
@@ -190,14 +106,7 @@ namespace tut
         // default combiner is defined to return the value returned by the
         // last listener, which is meaningless if there were no listeners.
         per_frame.post(0);
-        // NOTE: boost::bind() saves its arguments by VALUE! If you pass an
-        // object instance rather than a pointer, you'll end up binding to an
-        // internal copy of that instance! Use boost::ref() to capture a
-        // reference instead.
-        LLBoundListener connection = per_frame.listen(listener0.getName(),
-                                                      boost::bind(&Listener::call,
-                                                                  boost::ref(listener0),
-                                                                  _1));
+        LLBoundListener connection = listener0.listenTo(per_frame);
         ensure("connected", connection.connected());
         ensure("not blocked", ! connection.blocked());
         per_frame.post(1);
@@ -223,6 +132,10 @@ namespace tut
         bool threw = false;
         try
         {
+            // NOTE: boost::bind() saves its arguments by VALUE! If you pass
+            // an object instance rather than a pointer, you'll end up binding
+            // to an internal copy of that instance! Use boost::ref() to
+            // capture a reference instead.
             per_frame.listen(listener0.getName(), // note bug, dup name
                              boost::bind(&Listener::call, boost::ref(listener1), _1));
         }
@@ -237,8 +150,7 @@ namespace tut
         }
         ensure("threw DupListenerName", threw);
         // do it right this time
-        per_frame.listen(listener1.getName(),
-                         boost::bind(&Listener::call, boost::ref(listener1), _1));
+        listener1.listenTo(per_frame);
         per_frame.post(5);
         check_listener("got", listener0, 5);
         check_listener("got", listener1, 5);
@@ -268,16 +180,10 @@ namespace tut
         LLEventPump& per_frame(pumps.obtain("per-frame"));
         listener0.reset(0);
         listener1.reset(0);
-        LLBoundListener bound0 = per_frame.listen(listener0.getName(),
-                                                  boost::bind(&Listener::callstop,
-                                                              boost::ref(listener0),
-                                                              _1));
-        LLBoundListener bound1 = per_frame.listen(listener1.getName(),
-                                                  boost::bind(&Listener::call,
-                                                              boost::ref(listener1),
-                                                              _1),
-                                                  // after listener0
-                                                  make<LLEventPump::NameList>(list_of(listener0.getName())));
+        LLBoundListener bound0 = listener0.listenTo(per_frame, &Listener::callstop);
+        LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call,
+                                                    // after listener0
+                                                    make<LLEventPump::NameList>(list_of(listener0.getName())));
         ensure("enabled", per_frame.enabled());
         ensure("connected 0", bound0.connected());
         ensure("unblocked 0", ! bound0.blocked());
@@ -317,7 +223,7 @@ namespace tut
         // LLEventQueue.
         LLEventPump& mainloop(pumps.obtain("mainloop"));
         ensure("LLEventQueue leaf class", dynamic_cast<LLEventQueue*>(&login));
-        login.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1));
+        listener0.listenTo(login);
         listener0.reset(0);
         login.post(1);
         check_listener("waiting for queued event", listener0, 0);
@@ -370,11 +276,10 @@ namespace tut
     {
         set_test_name("stopListening()");
         LLEventPump& login(pumps.obtain("login"));
-        login.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1));
+        listener0.listenTo(login);
         login.stopListening(listener0.getName());
         // should not throw because stopListening() should have removed name
-        login.listen(listener0.getName(),
-                     boost::bind(&Listener::callstop, boost::ref(listener0), _1));
+        listener0.listenTo(login, &Listener::callstop);
         LLBoundListener wrong = login.getListener("bogus");
         ensure("bogus connection disconnected", ! wrong.connected());
         ensure("bogus connection blocked", wrong.blocked());
@@ -394,10 +299,8 @@ namespace tut
                         boost::bind(&LLEventPump::post, boost::ref(filter0), _1));
         upstream.listen(filter1.getName(),
                         boost::bind(&LLEventPump::post, boost::ref(filter1), _1));
-        filter0.listen(listener0.getName(),
-                       boost::bind(&Listener::call, boost::ref(listener0), _1));
-        filter1.listen(listener1.getName(),
-                       boost::bind(&Listener::call, boost::ref(listener1), _1));
+        listener0.listenTo(filter0);
+        listener1.listenTo(filter1);
         listener0.reset(0);
         listener1.reset(0);
         upstream.post(1);
@@ -552,7 +455,7 @@ namespace tut
         // Passing a string LLEventPump name to LLListenerOrPumpName
         listener0.reset(0);
         LLEventStream random("random");
-        random.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1));
+        listener0.listenTo(random);
         eventSource("random");
         check_listener("got by pump name", listener0, 17);
         bool threw = false;
@@ -754,6 +657,33 @@ namespace tut
         heaptest.post(2);
     }
 
+    template<> template<>
+    void events_object::test<15>()
+    {
+        // This test ensures that using an LLListenerWrapper subclass doesn't
+        // block Boost.Signals2 from recognizing a bound LLEventTrackable
+        // subclass.
+        set_test_name("listen(llwrap<LLLogListener>(boost::bind(...TempTrackableListener ref...)))");
+        bool live = false;
+        LLEventPump& heaptest(pumps.obtain("heaptest"));
+        LLBoundListener connection;
+        {
+            TempTrackableListener tempListener("temp", live);
+            ensure("TempTrackableListener constructed", live);
+            connection = heaptest.listen(tempListener.getName(),
+                                         llwrap<LLLogListener>(
+                                         boost::bind(&TempTrackableListener::call,
+                                                     boost::ref(tempListener), _1)));
+            heaptest.post(1);
+            check_listener("received", tempListener, 1);
+        } // presumably this will make tempListener go away?
+        // verify that
+        ensure("TempTrackableListener destroyed", ! live);
+        ensure("implicit disconnect", ! connection.connected());
+        // now just make sure we don't blow up trying to access a freed object!
+        heaptest.post(2);
+    }
+
     class TempSharedListener: public TempListener,
                               public boost::enable_shared_from_this<TempSharedListener>
     {
@@ -764,7 +694,7 @@ namespace tut
     };
 
     template<> template<>
-    void events_object::test<15>()
+    void events_object::test<16>()
     {
         set_test_name("listen(boost::bind(...TempSharedListener ref...))");
 #if 0