summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests/lleventdispatcher_test.cpp
AgeCommit message (Collapse)Author
2023-10-29DRTVWR-587: Fix LL::apply(function, LLSD array).Nat Goodspeed
We define a specialization of LLSDParam<const char*> to support passing an LLSD object to a const char* function parameter. Needless to remark, passing object.asString().c_str() would be Bad: destroying the temporary std::string returned by asString() would immediately invalidate the pointer returned by its c_str(). But when you pass LLSDParam<const char*>(object) as the parameter, that specialization itself stores the std::string so the c_str() pointer remains valid as long as the LLSDParam object does. Then there's LLSDParam<LLSD>, used when we don't have the parameter type available to select the LLSDParam specialization. LLSDParam<LLSD> defines a templated conversion operator T() that constructs an LLSDParam<T> to provide the actual parameter value. So far, so good. The trouble was with the implementation of LLSDParam<LLSD>: it constructed a _temporary_ LLSDParam<T>, implicitly called its operator T() and immediately destroyed it. Destroying LLSDParam<const char*> destroyed its stored string, thus invalidating the c_str() pointer before the target function was entered. Instead, make LLSDParam<LLSD>::operator T() capture each LLSDParam<T> it constructs, extending its lifespan to the lifespan of the LLSDParam<LLSD> instance. For this, derive each LLSDParam specialization from LLSDParamBase, a trivial base class that simply establishes the virtual destructor. We can then capture any specialization as a pointer to LLSDParamBase. Also restore LazyEventAPI tests on Mac.
2023-10-27DRTVWR-587: Skip Visual Studio LLSDParam<const char*> tests for now.Nat Goodspeed
They do work fine on clang... unblocking the rest of the team during diagnosis.
2023-07-27DRTVWR-587: Skip some tests that only fail with older Visual StudioNat Goodspeed
2023-07-13DRTVWR-558: Fix a few lleventdispatcher_test merge glitches.Nat Goodspeed
2023-07-13DRTVWR-558: Extend LLEventDispatcher::add() overloads.Nat Goodspeed
Add LL::always_return<T>(), which takes a callable and variadic arguments. It calls the callable with those arguments and, if the returned type is convertible to T, converts it and returns it. Otherwise it returns T(). always_return() is generalized from, and supersedes, LLEventDispatcher::ReturnLLSD. Add LL::function_arity<CALLABLE>, which extends boost::function_types::function_arity by reporting results for both std::function<CALLABLE> and boost::function<CALLABLE>. Use for LL::apply(function, LLSD array) as well as for LLEventDispatcher. Make LLEventDispatcher::add() overloads uniformly distinguish between a callable (whether non-static member function or otherwise) that accepts a single LLSD parameter, versus any other signature. Accepting exactly one LLSD parameter signals that the callable will accept the composite arguments LLSD blob, instead of asking LLEventDispatcher to unpack the arguments blob into individual arguments. Support add(subclass method) overloads for arbitrary-parameters methods as well as for (const LLSD&) methods. Update tests accordingly: we need no longer pass the boilerplate lambda instance getter that binds and returns 'this'. Extract to the two LLEventDispatcher::make_invoker() overloads the LL::apply() logic formerly found in ReturnLLSD. Change lleventdispatcher_test.cpp tests from boost::bind(), which accepts variadic arguments (even though it only passes a fixed set to the target callable), to fixed-signature lambdas. This is because the revamped add() overloads care about signature. Add a test for a non-static method that accepts (const LLSD&), in other words the composite arguments LLSD blob, and likewise returns LLSD. (cherry picked from commit 95b787f7d7226ee9de79dfc9816f33c8bf199aad)
2023-07-13DRTVWR-558: Add tests for batched LLDispatchListener operations.Nat Goodspeed
Specifically, add tests for: - successful map batch - map batch with some errors and a reply pump - map batch with some errors and no reply - successful array batch - array batch with some errors and a reply pump - array batch with some errors and no reply (cherry picked from commit 078f0f5c9fb5075a8ad01cac417e1d7ee2b6a919)
2023-07-13DRTVWR-558: Make DispatchResult methods use their arguments.Nat Goodspeed
Fix lleventdispatcher_test.cpp's test class DispatchResult::strfunc(), intfunc(), mapfunc() and arrayfunc() to return values derived from (not identical to) their arguments, so we can reuse these functions for further testing of passing arguments to a named callable. Adjust existing tests accordingly. (cherry picked from commit 07e09a8daea008d28b97399920db60a147cf75c0)
2023-07-13DRTVWR-558: Add tests for LLDispatchListener functionality.Nat Goodspeed
Refine the special case of calling a nullary target function from an (event) method, notably via LLDispatchListener. (cherry picked from commit edcc52a9f60b1ec9b8f53603d6e2676558d41294)
2023-07-13DRTVWR-558: Clean up LLEventDispatcher argument and result handling.Nat Goodspeed
Add a new LLEventDispatcher constructor accepting not only the map key to extract a requested function name, but a second map key to extract the arguments -- when required. In Doxygen comments, clarify the difference between the two constructors. Move interaction with the LLEventPump subsystem to LLDispatchListener. LLEventDispatcher is intended to be directly called. On error, instead of looking for a "reply" key in the invocation LLSD, throw DispatchError. Publish DispatchError, formerly an implementation detail, and its new subclass DispatchMissing. Make both LLEventDispatcher::operator()() overloads return LLSD, leveraging the new internal ReturnLLSD logic that returns a degenerate LLSD blob for a void target callable and, for compatible types, converts the returned value to LLSD. Notably, the public try_call() overloads still return bool; any value returned by the target callable is discarded. Clarify the operator() and try_call() argument requirements for target callables registered to accept an LLSD array, in Doxygen comments and in code. In particular, the 'event' passed to (event) overloads (vs. the (name, event) overloads) must be an LLSD map, so it must contain an "args" key (or the new arguments map key specified to the constructor) containing the LLSD args array. Since the use of the new args key depends on whether the target callable is registered to accept an array or a map, pass it into DispatchEntry::call() (and all subclass overrides), along with a bool to disambiguate whether we reached that method from an LLEventDispatcher (event) invocation method or a (name, event) invocation method. Allow streaming an LLEventDispatcher instance to std::ostream, primarily to facilitate construction of proper error messages. Revert the 'name' argument of internal try_call(key, name, event) to std::string. Ditch try_call_log(), try_call_one() and reply(). Fold try_call_one() logic into three-argument try_call(). Refactor callFail() as a template method accepting both the exception to throw and arbitrary stringize() arguments from which to construct the exception message. Non-static callFail() implicitly prepends the instance and a colon to the rest of the arguments, and calls static sCallFail(). The latter constructs the exception message, logs it and throws the specified exception. This obviates try_call_log(). Make implementation detail helper class LLSDArgsMapper a private member of LLEventDispatcher so it can access sCallFail(): we now want all error handling to go through that method. Add LLSDArgsMapper::callFail() resembling LLSDEventDispatcher::callFail(), but without having to specify the exception: only LLEventDispatcher will throw anything but generic DispatchError. Give LLEventDispatcher::ParamsDispatchEntry and its subclasses ArrayParamsDispatchEntry and MapParamsDispatchEntry a new 'name' argument to identify error messages. Store it and use it implicitly in new callFail() method, very like LLSDArgsMapper::callFail(). Make LLEventDispatcher:: addArrayParamsDispatchEntry() and addMapParamsDispatchEntry() pass a 'name' that includes the LLEventDispatcher instance name as well as the name of the specific registered callable. This way we need not intercept a low-level error and annotate it with contextual data: we can just let the exception propagate. Make ParamsDispatchEntry::call() override catch LL::apply_error thrown by an invoker_function, and pass its message to callFail(), i.e. rethrow as LLEventDispatcher::DispatchError. Introduce ArrayParamsDispatchEntry::call() override for the special logic to extract an arguments array from a passed LLSD map -- but only under the circumstances described in the Doxygen comment. Add similar logic to MapParamsDispatchEntry::call(), but with both argskey itself and a value for argskey optional in the passed LLSD map. Because LLEventDispatcher now has two constructor overloads, allow subclass constructor LLDispatchListener() to accept zero or more trailing arguments. This is different than giving LLDispatchListener's constructor a default final argument, in that the subclass doesn't need to specify its default value: that's up to the base-class constructor. But it does require that the subclass constructor move to the header file. Move private LLEventDispatcher::reply() method to LLDispatchListener. Extend LLDispatchListener::process() to handle DispatchError by attempting to reply with a map containing an "error" key, per convention. (In other words, move that logic from LLEventDispatcher to LLDispatchListener.) Also, for a map LLSD result, attempt to reply with that result; for other defined LLSD types, attempt to reply with a map containing a "data" key. This is backwards compatible with previous behavior because all previous LLDispatchListener subclass methods returned void, which now produces an undefined LLSD blob, which we don't bother trying to send in reply. In lleventdispatcher_test.cpp, rework tut::lleventdispatcher_data::call_exc() yet again to catch DispatchError instead of listening for an LLEventPump reply event. Similarly, make call_logerr() catch DispatchError. Since the exception should also be logged, we ignore it and focus on the log, as before. Add tests <23> to <27>, exercising calls to new class DispatchResult methods returning string, int, LLSD map, LLSD array and void. (cherry picked from commit 2f9c915dd3d5137b5b2b1a57f0179e1f7a090f8c)
2023-07-13DRTVWR-558: LLEventDispatcher uses LL::apply(), not boost::fusion.Nat Goodspeed
While calling a C++ function with arguments taken from a runtime-variable data structure necessarily involves a bit of hocus-pocus, the best you can say for the boost::fusion based implementation is that it worked. Sadly, template recursion limited its applicability to a handful of function arguments. Now that we have LL::apply(), use that instead. This implementation is much more straightforward. In particular, the LLSDArgsSource class, whose job was to dole out elements of an LLSD array one at a time for the template recursion, goes away entirely. Make virtual LLEventDispatcher::DispatchEntry::call() return LLSD instead of void. All LLEventDispatcher target functions so far have been void; any function that wants to respond to its invoker must do so explicitly by calling sendReply() or constructing an LLEventAPI::Response instance. Supporting non- void functions permits LLEventDispatcher to respond implicitly with the returned value. Of course this requires a wrapper for void target functions that returns LLSD::isUndefined(). Break out LLEventDispatcher::reply() from callFail(), so we can reply with success as well as failure. Make LLEventDispatcher::try_call_log() prepend the actual leaf class name and description to any error returned by three-arg try_call(). That try_call() overload reported "LLEventDispatcher(desc): " for a couple specific errors, but no others. Hoist to try_call_log() to apply uniformly. Introduce new try_call_one() method to diagnose name-not-found errors and catch internal DispatchError and LL::apply_error exceptions. try_call_one() returns a std::pair, containing either an error message or an LLSD value. Make try_call_log() and three-arg try_call() accept LLSD 'name' instead of plain std::string, allowing for the possibility of an array or map. That lets us extend three-arg try_call() to break out new cases for the function selector LLSD: isUndefined(), isArray(), isMap() and (current case) scalar String. If try_call_one() reports an error, log it and try to send reply, as now. If it returns LLSD::isUndefined(), e.g. from a void target function wrapper, do nothing. But if it returns an LLSD map, try to send that back to the invoker. And if it returns an LLSD scalar or array, wrap it in a map with key "data" to respond to the invoker. Allowing a target function to return its result rather than explicitly sending it opens the possibility of batched requests (aggregate 'name') returning batched responses. Almost every place that constructs LLEventDispatcher's internal DispatchError exception called stringize() to format the what() string. Simplify calls by making DispatchError accept variadic arguments and forward to stringize(). Add LL::invoke() to apply.h. Like LL::apply(), this is a (limited) C++14 foreshadowing of std::invoke(), with preprocessor conditionals to switch to std::invoke() when that's available. Introduce LL::invoke() to handle a callable that's actually a pointer to method. Now our C++14 apply() implementation can accept pointer to method, using invoke() to generalize the actual function call. Also anticipate std::bind_front() with LL::bind_front(). For apply(func, std::array) and our extensions apply(func, std::vector) and apply(func, LLSD), we can't pass a pointer to method as the func unless the second argument happens to be an array or vector of pointers (or references) to instances of exactly the right class -- and of course LLSD can't store such at all. It's tempting to pass std::bind(std::mem_fn(ptr_to_method), instance), but that won't work: std::bind() requires a value or placeholder for each argument to pass to the bound function. The bind() expression above would only work for a nullary method. std::bind_front() would work, but that doesn't arrive until C++20. Again, once we get there we'll defer to the std:: implementation. Instead of the generic __cplusplus, check the appropriate feature-test macro for availability of each of std::invoke(), std::apply() and std::bind_front(). Change apply() error handling from assert() to new LL::apply_error exception. LLEventDispatcher must be able to intercept apply() errors. Move validation and synthesis of the relevant error message to new apply.cpp source file. Add to llptrto.h new LL::get_ref() and LL::get_ptr() template functions to unify the cases of a calling template accepting either a pointer or a reference. Wrapping the parameter in either get_ref() or get_ptr() allows dereferencing the parameter as desired. Move LL::apply(function, LLSD) argument validation/manipulation to a non- template function in llsdutil.cpp: no need to replicate that logic in the template for every CALLABLE specialization. The trouble with passing bind_front(std::mem_fn(ptr_to_method), instance) to apply() is that since bind_front() accepts and forwards variadic additional arguments, apply() can't infer the arity of the bound ptr_to_method. Address that by introducing apply_n<arity>(function, LLSD), permitting a caller to infer the arity of ptr_to_method and explicitly pass it to apply_n(). Polish up lleventdispatcher_test.cpp accordingly. Wrong LLSD type and wrong number of arguments now produce different (somewhat more informative) error messages. Moreover, passing too many entries in an LLSD array used to work: the extra arguments used to be ignored. Now we require that the size of the array match the arity of the target function. Change the too-many-arguments tests from success testing to error testing. Replace 'foreach' aka BOOST_FOREACH macro invocations with range 'for'. Replace STRINGIZE(item0 << item1 << ...) with stringize(item0, item1, ...). (cherry picked from commit 9c049563b5480bb7e8ed87d9313822595b479c3b)
2023-07-12SL-18330: Merge commit '6b53036' into DRTVWR-587-maint-VNat Goodspeed
Bring over part of the LLEventDispatcher work inspired by DRTVWR-558.
2023-05-04Merge remote-tracking branch 'origin/zap-LLSDArray' into DRTVWR-577-maint-SAndrey Lihatskiy
2023-05-03SL-19647: Eliminate LLSDArray entirely.Nat Goodspeed
Newer C++ compilers have different semantics around LLSDArray's special copy constructor, which was essential to proper LLSD nesting. In short, we can no longer trust LLSDArray to behave correctly. Now that we have variadic functions, get rid of LLSDArray and replace every reference with llsd::array().
2022-11-03DRTVWR-575: A few more tweaks addressing size_t wider than 32 bits.Nat Goodspeed
2022-06-15DRTVWR-558: Change LLEventDispatcher error action (also LLEventAPI).Nat Goodspeed
Originally the LLEventAPI mechanism was primarily used for VITA testing. In that case it was okay for the viewer to crash with LL_ERRS if the test script passed a bad request. With puppetry, hopefully new LEAP scripts will be written to engage LLEventAPIs in all sorts of interesting ways. Change error handling from LL_ERRS to LL_WARNS. Furthermore, if the incoming request contains a "reply" key, send back an error response to the requester. Update lleventdispatcher_test.cpp accordingly. (cherry picked from commit de0539fcbe815ceec2041ecc9981e3adf59f2806)
2020-04-03DRTVWR-476: Cherry-pick debug aids from commit 77b0c53 (fiber-mutex)Nat Goodspeed
2020-03-25[DRTVWR-476] - fix compiler errors 32 bit windows buildAnchor
2019-08-10DRTVWR-493: Introduce test catch_what(), catch_llerrs() functions.Nat Goodspeed
Use them in place of awkward try/catch test boilerplate.
2015-11-10remove execute permission from many files that should not have itOz Linden
2013-10-21another attempted buildfixRichard Linden
2013-03-29Update Mac and Windows breakpad builds to latestGraham Madarasz
2011-02-09Add test to call map-style functions with full map/array params.Nat Goodspeed
Test also passes overlong arrays and maps with extraneous keys; in all cases we expect the same set of values to be passed to the registered functions.
2011-02-07For test purposes, capture at registration each function's Vars*.Nat Goodspeed
We'd introduced FunctionsTriple to associate a pair of registered function names with the Vars* on which those functions should operate. But with more different tests coming up, it became clear that restating the Vars* every time a given function name appeared in any such context was redundant. Instead, extended addf() to accept and store the relevant Vars* for each registered function, be it the global Vars for the free functions and static methods or the stack Vars for the non-static methods. Added varsfor() function to retrieve and validate the Vars* for a given function name. Eliminated array_funcs() function, restating aggregates of names to test as LLSD collections. Where before these were coerced into a separate LLSD map with ["a"] and ["b"] keys, that map can now be part of the original structure.
2011-02-06Untested support for passing array to map-registered function.Nat Goodspeed
An array-registered function has no param names, so you can only pass an array: a map would be meaningless. Initial implementation of map-registered functions assumed that since you CAN pass a map, you MUST pass a map. But in fact it's meaningful to pass an array as well -- for whatever reason -- and easy to implement, so there you are. Tests to follow.
2011-02-06Add test verifying passing LLSD() to const char* parameter.Nat Goodspeed
LLSDParam<const char*> is coded to pass NULL for an isUndefined() LLSD value, so event-based caller can choose whether to pass NULL, "" or whatever string value to such a parameter. Ensure this behavior.
2011-02-05Introduce zipmap() function and use it in place of frequent loops.Nat Goodspeed
One operation we often use is to take an LLSD array of param names, a corresponding LLSD array of values, and create from them a name=value LLSD map. Instead of doing that "by hand" every time, use a function.
2011-02-05Make array-funcs success test exercise args-array-too-long case too.Nat Goodspeed
Streamline a bit more redundancy from the code in that test.
2011-02-05Consolidate paramsa, paramsb, et al., into ["a"], ["b"] arrays.Nat Goodspeed
Following the C++ convention of having two distinct somethigna, somethingb names, initially we introduced paramsa, paramsb LLSD arrays, following that convention all the way down the line. This led to two distinct loops every time we wanted to walk both arrays, since we didn't want to assume that they were both the same size. But leveraging the fact that distinct LLSD arrays stored in the same LLSD container can in fact be of different lengths, refactored all the pairs of vars into top-level LLSD maps keyed by ["a"] and ["b"]. That lets us perform nested loops rather than duplicating the logic, making test code much less messy.
2011-02-05Fix Vars::cp dangling-pointer problem.Nat Goodspeed
Naively storing a const char* param in a const char* data member ignores the fact that once the caller's done, the string data referenced by that pointer will probably be freed. Store the referenced string in a std::string instead.
2011-02-05Add successful calls to array-style functions.Nat Goodspeed
2011-02-04Change FunctionsTriple refs to pointers to facilitate passing.Nat Goodspeed
A certain popular-but-dumb compiler seems to think that initializing a std::vector from a pair of iterators requires assignment. A struct containing a reference cannot be assigned. Pointers get us past this issue.
2011-02-04Move FunctionsTriple data to function returning vector<same>.Nat Goodspeed
We want to break out a couple different test methods that both need the same data. While we could define a std::vector<FunctionsTriple> in the lleventdispatcher_data class and initialize it using a classic {} initializer as in array_funcs(), using a separate function puts it closer to the tests consuming that data, and helps reduce clutter in the central data class. Either way, it's cool that BOOST_FOREACH() handles the gory details of iterating over a std::vector vs. a classic-C array.
2011-02-03BOOST_FOREACH(LLSD) helpers more readable with 'using namespace'.Nat Goodspeed
2011-02-03Introduce BOOST_FOREACH() helpers for LLSD in llsdutil.h.Nat Goodspeed
You can't directly write: BOOST_FOREACH(LLSD item, someLLSDarray) { ... } because LLSD has two distinct iteration mechanisms, one for arrays and one for maps, neither using the standard [const_]iterator typedefs or begin()/end() methods. But with these helpers, you can write: BOOST_FOREACH(LLSD item, llsd::inArray(someLLSDarray)) { ... } or BOOST_FOREACH(const llsd::MapEntry& pair, llsd::inMap(someLLSDmap)) { ... } These are in namespace llsd instead of being (e.g.) llsd_inMap because with a namespace at least your .cpp file can have a local 'using': using namespace llsd; BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... } It's namespace llsd rather than LLSD because LLSD can't be both a namespace and a class name.
2011-02-03Add test to call array-style functions with too-short array.Nat Goodspeed
Also, finally got sick of hand-writing the official iterator-loop idiom and dragged in BOOST_FOREACH(). Because LLSD has two completely different iteration styles, added inArray and inMap helper classes to be able to write: BOOST_FOREACH(LLSD item, inArray(someArray)) { ... }
2011-02-02Add test to call no-args functions using (map | array)-style callsNat Goodspeed
2011-02-02Add test to exercise map/array args mismatch validation.Nat Goodspeed
2011-02-02First few LLEventDispatcher call cases: try_call(), call CallablesNat Goodspeed
2011-02-01Replace ad-hoc test functions/methods with systematic enumeration.Nat Goodspeed
Previous tests involved a small handful of functions with only a couple different parameter types. Now we exhaustively invoke every registration case, plus every metadata query case. Call cases still pending.
2011-01-28Extend LLEventAPI to directly call other functions & methods.Nat Goodspeed
Until now, LLEventAPI has only been able to register functions specifically accepting(const LLSD&). Typically you add a wrapper method to your LLEventAPI subclass, register that, have it extract desired params from the incoming LLSD and then call the actual function of interest. With help from Alain, added new LLEventAPI::add() methods capable of registering functions/methods with arbitrary parameter signatures. The code uses boost::fusion magic to implicitly match incoming LLSD arguments to the function's formal parameter list, bypassing the need for an explicit helper method. New add() methods caused an ambiguity with a previous convenience overload. Removed that overload and fixed the one existing usage. Replaced LLEventDispatcher::get() with try_call() -- it's no longer easy to return a Callable for caller to call directly. But the one known use of that feature simply used it to avoid fatal LL_ERRS on unknown function-name string, hence the try_call() approach actually addresses that case more directly. Added indra/common/lleventdispatcher_test.cpp to exercise new functionality.