summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2022-06-21 15:23:29 -0400
committerNat Goodspeed <nat@lindenlab.com>2022-06-21 15:23:29 -0400
commitfdc0257acbde5a2d5bb201efcc8bb723df09daf8 (patch)
treee2ad75447d0f54777814744a00b3234a5c5a1d43
parent490de3ab6e3a2b9dd8668c2093e265f36324f82e (diff)
DRTVWR-564: Fix LLEventDispatcher::addMethod() for LazyEventAPI.
A classic LLEventAPI subclass calls LLEventDispatcher::add() methods in its own constructor. At that point, addMethod() can reliably dynamic_cast its 'this' pointer to the new subclass. But because of the way LazyEventAPI queues up add() calls, they're invoked in the (new) LLEventAPI constructor itself. The subclass constructor body hasn't even started running, and LLEventDispatcher::addMethod()'s dynamic_cast to the LLEventAPI subclass returns nullptr. addMethod() claims the new subclass isn't derived from LLEventDispatcher, which is confusing since it is. It works to change addMethod()'s dynamic_cast to static_cast. Flesh out lazyeventapi_test.cpp. post() maps with "op" keys to actually try to engage the registered operation. Give the operation an observable side effect; use ensure_mumble() to verify. Also verify that LazyEventAPI has captured the subject LLEventAPI's metadata in a way we can retrieve.
-rw-r--r--indra/llcommon/lleventdispatcher.h2
-rw-r--r--indra/llcommon/tests/lazyeventapi_test.cpp47
2 files changed, 42 insertions, 7 deletions
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index ce9d3775cc..2e140329f3 100644
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -329,7 +329,7 @@ private:
void addMethod(const std::string& name, const std::string& desc,
const METHOD& method, const LLSD& required)
{
- CLASS* downcast = dynamic_cast<CLASS*>(this);
+ CLASS* downcast = static_cast<CLASS*>(this);
if (! downcast)
{
addFail(name, typeid(CLASS).name());
diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp
index 6639c5e540..4c78fd7105 100644
--- a/indra/llcommon/tests/lazyeventapi_test.cpp
+++ b/indra/llcommon/tests/lazyeventapi_test.cpp
@@ -19,18 +19,25 @@
// other Linden headers
#include "../test/lltut.h"
#include "llevents.h"
+#include "llsdutil.h"
+
+// observable side effect, solely for testing
+static LLSD data;
// LLEventAPI listener subclass
class MyListener: public LLEventAPI
{
public:
+ // need this trivial forwarding constructor
+ // (of course do any other initialization your subclass requires)
MyListener(const LL::LazyEventAPIParams& params):
LLEventAPI(params)
{}
- void get(const LLSD& event)
+ // example operation, registered by LazyEventAPI subclass below
+ void set_data(const LLSD& event)
{
- std::cout << "MyListener::get() got " << event << std::endl;
+ data = event["data"];
}
};
@@ -40,14 +47,17 @@ class MyRegistrar: public LL::LazyEventAPI<MyListener>
using super = LL::LazyEventAPI<MyListener>;
using super::listener;
public:
+ // LazyEventAPI subclass initializes like a classic LLEventAPI subclass
+ // constructor, with API name and desc plus add() calls for the defined
+ // operations
MyRegistrar():
super("Test", "This is a test LLEventAPI")
{
- add("get", "This is a get operation", &listener::get);
+ add("set", "This is a set operation", &listener::set_data);
}
};
// Normally we'd declare a static instance of MyRegistrar -- but because we
-// may want to test with and without, defer declaration to individual test
+// want to test both with and without, defer declaration to individual test
// methods.
/*****************************************************************************
@@ -57,6 +67,11 @@ namespace tut
{
struct lazyeventapi_data
{
+ lazyeventapi_data()
+ {
+ // before every test, reset 'data'
+ data.clear();
+ }
~lazyeventapi_data()
{
// after every test, reset LLEventPumps
@@ -74,7 +89,8 @@ namespace tut
// this is where the magic (should) happen
// 'register' still a keyword until C++17
MyRegistrar regster;
- LLEventPumps::instance().obtain("Test").post("hey");
+ LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey"));
+ ensure_equals("failed to set data", data.asString(), "hey");
}
template<> template<>
@@ -84,6 +100,25 @@ namespace tut
// Because the MyRegistrar declaration in test<1>() is local, because
// it has been destroyed, we fully expect NOT to reach a MyListener
// instance with this post.
- LLEventPumps::instance().obtain("Test").post("moot");
+ LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot"));
+ ensure("accidentally set data", ! data.isDefined());
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("LazyEventAPI metadata");
+ MyRegistrar regster;
+ const MyRegistrar* found = nullptr;
+ for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
+ if ((found = dynamic_cast<const MyRegistrar*>(&registrar)))
+ break;
+ ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
+ ensure_equals("wrong API name", found->mParams.name, "Test");
+ ensure_contains("wrong API desc", found->mParams.desc, "test LLEventAPI");
+ ensure_equals("wrong API field", found->mParams.field, "op");
+ ensure_equals("failed to find operations", found->mOperations.size(), 1);
+ ensure_equals("wrong operation name", found->mOperations[0].first, "set");
+ ensure_contains("wrong operation desc", found->mOperations[0].second, "set operation");
}
} // namespace tut