summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests/classic_callback_test.cpp
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2021-09-23 16:25:12 -0400
committerNat Goodspeed <nat@lindenlab.com>2021-09-23 16:25:12 -0400
commit2b96d1bbe00c317ea8dfe420dd4167dde5d153ae (patch)
tree4419e48a4d1470f8ef69d7727ed38bf281f51535 /indra/llcommon/tests/classic_callback_test.cpp
parentc40b8310b00f1d620a2f5a94f2514c8bf3531373 (diff)
DRTVWR-543: Add ClassicCallback utility class with tests
Diffstat (limited to 'indra/llcommon/tests/classic_callback_test.cpp')
-rw-r--r--indra/llcommon/tests/classic_callback_test.cpp150
1 files changed, 150 insertions, 0 deletions
diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp
new file mode 100644
index 0000000000..232532b1c1
--- /dev/null
+++ b/indra/llcommon/tests/classic_callback_test.cpp
@@ -0,0 +1,150 @@
+/**
+ * @file classic_callback_test.cpp
+ * @author Nat Goodspeed
+ * @date 2021-09-22
+ * @brief Test ClassicCallback and HeapClassicCallback.
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Copyright (c) 2021, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "classic_callback.h"
+// STL headers
+#include <iostream>
+#include <string>
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+
+/*****************************************************************************
+* example callback accepting only (void* userdata)
+*****************************************************************************/
+// callback_t is part of the specification of someAPI()
+typedef void (*callback_t)(const char*, void*);
+void someAPI(callback_t callback, void* userdata)
+{
+ callback("called", userdata);
+}
+
+// C++ callable I want as the actual callback
+struct MyCallback
+{
+ void operator()(const char* msg, void*)
+ {
+ mMsg = msg;
+ }
+
+ void callback_with_extra(const std::string& extra, const char* msg)
+ {
+ mMsg = extra + ' ' + msg;
+ }
+
+ std::string mMsg;
+};
+
+// a function for which I want to bind other data
+void callback_with_extra(const std::string& extra, void*)
+{
+ std::cout << "callback_with_extra('" << extra << "', *)\n";
+}
+
+/*****************************************************************************
+* example callback accepting several params, and void* userdata isn't first
+*****************************************************************************/
+typedef std::string (*complex_callback)(int, const char*, void*, double);
+std::string otherAPI(complex_callback callback, void* userdata)
+{
+ return callback(17, "hello world", userdata, 3.0);
+}
+
+// struct into which we can capture complex_callback params
+static struct Data
+{
+ void set(int i, const char* s, double f)
+ {
+ mi = i;
+ ms = s;
+ mf = f;
+ }
+
+ void clear() { set(0, "", 0.0); }
+
+ int mi;
+ std::string ms;
+ double mf;
+} sData;
+
+// C++ callable I want to pass
+struct OtherCallback
+{
+ std::string operator()(int num, const char* str, void*, double approx)
+ {
+ sData.set(num, str, approx);
+ return "hello back!";
+ }
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct classic_callback_data
+ {
+ };
+ typedef test_group<classic_callback_data> classic_callback_group;
+ typedef classic_callback_group::object object;
+ classic_callback_group classic_callbackgrp("classic_callback");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("ClassicCallback");
+ // engage someAPI(MyCallback())
+ auto ccb{ makeClassicCallback<callback_t>(MyCallback()) };
+ someAPI(ccb.get_callback(), ccb.get_userdata());
+ // Unfortunately, with the side effect confined to the bound
+ // MyCallback instance, that call was invisible. Bind a reference to a
+ // named instance by specifying a ref type.
+ MyCallback mcb;
+ ClassicCallback<callback_t, void*, MyCallback&> ccb2(mcb);
+ someAPI(ccb2.get_callback(), ccb2.get_userdata());
+ ensure_equals("failed to call through ClassicCallback", mcb.mMsg, "called");
+
+ // try with HeapClassicCallback
+ mcb.mMsg.clear();
+ auto hcbp{ makeHeapClassicCallback<callback_t>(mcb) };
+ someAPI(hcbp->get_callback(), hcbp->get_userdata());
+ ensure_equals("failed to call through HeapClassicCallback", mcb.mMsg, "called");
+
+ // lambda
+ // The tricky thing here is that a lambda is an unspecified type, so
+ // you can't declare a ClassicCallback<signature, void*, that type>.
+ mcb.mMsg.clear();
+ auto xcb(
+ makeClassicCallback<callback_t>(
+ [&mcb](const char* msg, void*)
+ { mcb.callback_with_extra("extra", msg); }));
+ someAPI(xcb.get_callback(), xcb.get_userdata());
+ ensure_equals("failed to call lambda", mcb.mMsg, "extra called");
+
+ // engage otherAPI(OtherCallback())
+ OtherCallback ocb;
+ // Instead of specifying a reference type for the bound CALLBACK, as
+ // with ccb2 above, you can alternatively move the callable object
+ // into the ClassicCallback (of course AFTER any other reference).
+ // That's why OtherCallback uses external data for its observable side
+ // effect.
+ auto occb{ makeClassicCallback<complex_callback>(std::move(ocb)) };
+ std::string result{ otherAPI(occb.get_callback(), occb.get_userdata()) };
+ ensure_equals("failed to return callback result", result, "hello back!");
+ ensure_equals("failed to set int", sData.mi, 17);
+ ensure_equals("failed to set string", sData.ms, "hello world");
+ ensure_equals("failed to set double", sData.mf, 3.0);
+ }
+} // namespace tut