diff options
| author | Mnikolenko Productengine <mnikolenko@productengine.com> | 2022-06-29 21:02:57 +0300 | 
|---|---|---|
| committer | Mnikolenko Productengine <mnikolenko@productengine.com> | 2022-06-29 21:03:15 +0300 | 
| commit | b0e98406488f712220175141f2b0f45a95082f12 (patch) | |
| tree | 4a965843d24f984caf12722c7809a84797ddc212 /indra/llcommon/tests | |
| parent | 76c8a7ff0b57e7856613960391be7c400384183b (diff) | |
| parent | 1e4f2ec07e32a142f35817d3186a124df3f8cd25 (diff) | |
Merge branch 'master' into DRTVWR-539
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/classic_callback_test.cpp | 144 | 
1 files changed, 144 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..c060775c24 --- /dev/null +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -0,0 +1,144 @@ +/** + * @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 +*****************************************************************************/ +// 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; +}; + +/***************************************************************************** +*   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 | 
