diff --git a/include/dsn/utility/join_point.h b/include/dsn/utility/join_point.h index 971072d7d1..0a90ffc726 100644 --- a/include/dsn/utility/join_point.h +++ b/include/dsn/utility/join_point.h @@ -24,454 +24,98 @@ * THE SOFTWARE. */ -/* - * Description: - * join point definition for various prototypes - * - * Revision history: - * Mar., 2015, @imzhenyu (Zhenyu Guo), first version - * xxxx-xx-xx, author, fix bug about xxx - */ - #pragma once -#include +#include +#include +#include +#include +#include namespace dsn { +// A join_point instance is a set of lambdas with the identical function signature. +// It's typically used for creating hooks at the specific execution points, +// for example: +// - on rpc session establishes +// - on process begins to exits +// - ... +// Using join_point, we can inject the behavior in these cases in non-intrusive way. +// +// NOTE: "Join point" is a concept in Aspect-Oriented-Programming. Each "advice" is +// an extension on the join-point. It's similar with the "Interceptor Pattern". +// - https://en.wikipedia.org/wiki/Advice_(programming) + +template class join_point_base { public: - join_point_base(const char *name); - - bool put_front(void *fn, const char *name, bool is_native = false); - bool put_back(void *fn, const char *name, bool is_native = false); - bool put_before(const char *base, void *fn, const char *name, bool is_native = false); - bool put_after(const char *base, void *fn, const char *name, bool is_native = false); - bool remove(const char *name); - bool put_replace(const char *base, void *fn, const char *name); - - const char *name() const { return _name.c_str(); } - -protected: - struct advice_entry - { - std::string name; - void *func; - bool is_native; - advice_entry *next; - advice_entry *prev; - }; - - advice_entry _hdr; - std::string _name; - -private: - advice_entry *new_entry(void *fn, const char *name, bool is_native); - advice_entry *get_by_name(const char *name); -}; - -struct join_point_unused_type -{ -}; - -template -class join_point; - -template -class join_point : public join_point_base -{ -public: - typedef TReturn (*point_prototype)(T1, T2, T3); - typedef void (*advice_prototype)(T1, T2, T3); + explicit join_point_base(const char *name) : _name(name) {} -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } - - TReturn execute(T1 p1, T2 p2, T3 p3, TReturn default_return_value) - { - TReturn returnValue = default_return_value; - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - returnValue = (*(point_prototype *)&p->func)(p1, p2, p3); - } else { - (*(advice_prototype *)&p->func)(p1, p2, p3); - } - p = p->next; - } - return returnValue; - } -}; - -template -class join_point : public join_point_base -{ -public: - typedef void (*point_prototype)(T1, T2, T3); - typedef void (*advice_prototype)(T1, T2, T3); - -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } - - void execute(T1 p1, T2 p2, T3 p3) - { - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - (*(point_prototype *)&p->func)(p1, p2, p3); - } else { - (*(advice_prototype *)&p->func)(p1, p2, p3); - } - p = p->next; - } - } -}; - -template -class join_point : public join_point_base -{ -public: - typedef TReturn (*point_prototype)(T1, T2); - typedef void (*advice_prototype)(T1, T2); - -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } - - TReturn execute(T1 p1, T2 p2, TReturn default_return_value) - { - TReturn returnValue = default_return_value; - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - returnValue = (*(point_prototype *)&p->func)(p1, p2); - } else { - (*(advice_prototype *)&p->func)(p1, p2); - } - p = p->next; - } - return returnValue; - } -}; + using ReturnedAdviceT = R(Args...); + using AdviceT = void(Args...); -template -class join_point : public join_point_base -{ -public: - typedef void (*point_prototype)(T1, T2); - typedef void (*advice_prototype)(T1, T2); + // TODO(wutao): call it add_returned_advice() + void put_native(std::function fn) { _ret_advice_entries.push_front(fn); } -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) + // TODO(wutao): call it add_advice() + void put_back(std::function fn, const char * /*unused*/) { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); + _advice_entries.push_back(std::move(fn)); } - void execute(T1 p1, T2 p2) + void put_front(std::function fn, const char * /*unused*/) { - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - (*(point_prototype *)&p->func)(p1, p2); - } else { - (*(advice_prototype *)&p->func)(p1, p2); - } - p = p->next; - } + _advice_entries.push_front(std::move(fn)); } -}; -template -class join_point - : public join_point_base -{ -public: - typedef TReturn (*point_prototype)(T1); - typedef void (*advice_prototype)(T1); + const char *name() const { return _name.c_str(); } -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } +protected: + std::list> _ret_advice_entries; + std::list> _advice_entries; + const std::string _name; - TReturn execute(T1 p1, TReturn default_return_value) - { - TReturn returnValue = default_return_value; - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - returnValue = (*(point_prototype *)&p->func)(p1); - } else { - (*(advice_prototype *)&p->func)(p1); - } - p = p->next; - } - return returnValue; - } +private: + friend class join_point_test; }; -template -class join_point : public join_point_base +template +class join_point final : public join_point_base { public: - typedef void (*point_prototype)(T1); - typedef void (*advice_prototype)(T1); + using BaseType = join_point_base; + static_assert(!std::is_void::value, "type R must not be a void"); -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } + explicit join_point(const char *name) : BaseType(name) {} - void execute(T1 p1) + // Execute the hooks sequentially. + R execute(Args... args, R default_return_value) { - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - (*(point_prototype *)&p->func)(p1); - } else { - (*(advice_prototype *)&p->func)(p1); - } - p = p->next; + R ret = default_return_value; + for (auto &func : BaseType::_ret_advice_entries) { + ret = dsn::apply(func, std::make_tuple(std::forward(args)...)); } - } -}; - -template -class join_point - : public join_point_base -{ -public: - typedef TReturn (*point_prototype)(); - typedef void (*advice_prototype)(); - -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } - - TReturn execute(TReturn default_return_value) - { - TReturn returnValue = default_return_value; - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - returnValue = (*(point_prototype *)&p->func)(); - } else { - (*(advice_prototype *)&p->func)(); - } - p = p->next; + for (auto &func : BaseType::_advice_entries) { + dsn::apply(func, std::make_tuple(std::forward(args)...)); } - return returnValue; + return ret; } }; -template <> -class join_point - : public join_point_base +template +class join_point final : public join_point_base { public: - typedef void (*point_prototype)(); - typedef void (*advice_prototype)(); + using BaseType = join_point_base; -public: - join_point(const char *name) : join_point_base(name) {} - bool put_native(point_prototype point) - { - return join_point_base::put_front((void *)point, "native", true); - } - bool put_front(advice_prototype fn, const char *name) - { - return join_point_base::put_front((void *)fn, name); - } - bool put_back(advice_prototype fn, const char *name) - { - return join_point_base::put_back((void *)fn, name); - } - bool put_before(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_before(base, (void *)fn, name); - } - bool put_after(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_after(base, (void *)fn, name); - } - bool put_replace(const char *base, advice_prototype fn, const char *name) - { - return join_point_base::put_replace(base, (void *)fn, name); - } + explicit join_point(const char *name) : BaseType(name) {} - void execute() + // Execute the hooks sequentially. + void execute(Args... args) { - advice_entry *p = _hdr.next; - while (p != &_hdr) { - if (p->is_native) { - (*(point_prototype *)&p->func)(); - } else { - (*(advice_prototype *)&p->func)(); - } - p = p->next; + for (auto &func : BaseType::_advice_entries) { + dsn::apply(func, std::make_tuple(std::forward(args)...)); } } }; diff --git a/src/utils/join_point.cpp b/src/utils/join_point.cpp deleted file mode 100644 index bfb0095e88..0000000000 --- a/src/utils/join_point.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Microsoft Corporation - * - * -=- Robust Distributed System Nucleus (rDSN) -=- - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Description: - * What is this file about? - * - * Revision history: - * xxxx-xx-xx, author, first version - * xxxx-xx-xx, author, fix bug about xxx - */ - -#include -#include - -namespace dsn { - -join_point_base::join_point_base(const char *name) -{ - _name = std::string(name); - _hdr.next = _hdr.prev = &_hdr; - _hdr.name = ""; -} - -bool join_point_base::put_front(void *fn, const char *name, bool is_native) -{ - auto e = new_entry(fn, name, is_native); - auto e1 = _hdr.next; - - _hdr.next = e; - e->next = e1; - e1->prev = e; - e->prev = &_hdr; - - return true; -} - -bool join_point_base::put_back(void *fn, const char *name, bool is_native) -{ - auto e = new_entry(fn, name, is_native); - auto e1 = _hdr.prev; - - e1->next = e; - e->next = &_hdr; - _hdr.prev = e; - e->prev = e1; - - return true; -} - -bool join_point_base::put_before(const char *base, void *fn, const char *name, bool is_native) -{ - auto e0 = get_by_name(base); - if (e0 == nullptr) { - dassert(false, "cannot find advice with name '%s' in '%s'", base, _name.c_str()); - return false; - } - - auto e = new_entry(fn, name, is_native); - - auto e1 = e0->prev; - e1->next = e; - e->next = e0; - e0->prev = e; - e->prev = e1; - - return true; -} - -bool join_point_base::put_after(const char *base, void *fn, const char *name, bool is_native) -{ - auto e0 = get_by_name(base); - if (e0 == nullptr) { - dassert(false, "cannot find advice with name '%s' in '%s'", base, _name.c_str()); - return false; - } - - auto e = new_entry(fn, name, is_native); - - auto e1 = e0->next; - e1->prev = e; - e->prev = e0; - e0->next = e; - e->next = e1; - - return true; -} - -bool join_point_base::put_replace(const char *base, void *fn, const char *name) -{ - auto e0 = get_by_name(base); - if (e0 == nullptr) { - dassert(false, "cannot find advice with name '%s' in '%s'", base, _name.c_str()); - return false; - } else { - e0->func = fn; - e0->name = name; - return true; - } -} - -bool join_point_base::remove(const char *name) -{ - auto e0 = get_by_name(name); - if (e0 == nullptr) { - dassert(false, "cannot find advice with name '%s' in '%s'", name, _name.c_str()); - return false; - } - - e0->next->prev = e0->prev; - e0->prev->next = e0->next; - - return true; -} - -join_point_base::advice_entry * -join_point_base::new_entry(void *fn, const char *name, bool is_native) -{ - auto e = new advice_entry(); - e->name = std::string(name); - e->func = fn; - e->is_native = is_native; - e->next = e->prev = e; - return e; -} - -join_point_base::advice_entry *join_point_base::get_by_name(const char *name) -{ - auto p = _hdr.next; - while (p != &_hdr) { - if (strcmp(name, p->name.c_str()) == 0) - return p; - - p = p->next; - } - - return nullptr; -} - -} // end namespace dsn diff --git a/src/utils/test/join_point.cpp b/src/utils/test/join_point.cpp deleted file mode 100644 index 0bb94ca7b2..0000000000 --- a/src/utils/test/join_point.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Microsoft Corporation - * - * -=- Robust Distributed System Nucleus (rDSN) -=- - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Description: - * Unit-test for join point. - * - * Revision history: - * Nov., 2015, @qinzuoyan (Zuoyan Qin), first version - * xxxx-xx-xx, author, fix bug about xxx - */ - -#include -#include - -using namespace ::dsn; - -struct entry -{ - std::string name; - void *func; - bool is_native; - entry(const std::string &name_, void *func_, bool is_native_) - : name(name_), func(func_), is_native(is_native_) - { - } - bool operator==(const entry &e) const - { - return name == e.name && func == e.func && is_native == e.is_native; - } -}; - -class join_point_for_test : public join_point_base -{ -public: - join_point_for_test() : join_point_base("join_point_for_test") {} - void get_all(std::vector &vec) const - { - vec.clear(); - advice_entry *p = _hdr.next; - while (p != &_hdr) { - vec.push_back(entry(p->name, p->func, p->is_native)); - p = p->next; - } - } -}; - -TEST(core, join_point) -{ - join_point_for_test jp; - std::vector check_vec, jp_vec; - - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - - { // [1] - entry e("1", (void *)1, false); - bool r = jp.put_front(e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1] - entry e("2", (void *)2, true); - bool r = jp.put_front(e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - std::swap(check_vec[0], check_vec[1]); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3] - entry e("3", (void *)3, false); - bool r = jp.put_back(e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3,4] - entry e("4", (void *)4, true); - bool r = jp.put_back(e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3,5,4] - entry e("5", (void *)5, false); - bool r = jp.put_before("4", e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - std::swap(check_vec[3], check_vec[4]); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3,5,4,6] - entry e("6", (void *)6, true); - bool r = jp.put_after("4", e.func, e.name.c_str(), e.is_native); - ASSERT_TRUE(r); - check_vec.push_back(e); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3,5,4] - bool r = jp.remove("6"); - ASSERT_TRUE(r); - check_vec.pop_back(); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,3,4] - bool r = jp.remove("5"); - ASSERT_TRUE(r); - std::swap(check_vec[3], check_vec[4]); - check_vec.pop_back(); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } - - { // [2,1,7,4] - entry e("7", (void *)7, false); - bool r = jp.put_replace("3", e.func, e.name.c_str()); - ASSERT_TRUE(r); - std::swap(check_vec[2], e); - jp.get_all(jp_vec); - ASSERT_EQ(check_vec, jp_vec); - } -} diff --git a/src/utils/test/join_point_test.cpp b/src/utils/test/join_point_test.cpp new file mode 100644 index 0000000000..0bc7f6da44 --- /dev/null +++ b/src/utils/test/join_point_test.cpp @@ -0,0 +1,87 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Microsoft Corporation + * + * -=- Robust Distributed System Nucleus (rDSN) -=- + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +namespace dsn { + +class join_point_test : public ::testing::Test +{ +public: +}; + +void advice1() {} + +// smoke test +TEST_F(join_point_test, add_pure_functions) +{ + join_point jp("test"); + jp.put_back(advice1, "test"); + jp.put_front(advice1, "test"); + jp.put_native(advice1); + jp.execute(); + + ASSERT_STREQ(jp.name(), "test"); +} + +TEST_F(join_point_test, adding_order) +{ + join_point jp("test"); + + std::vector vec; + jp.put_back([&](int val) { vec.push_back(1); }, "test"); + jp.put_back([&](int val) { vec.push_back(2); }, "test"); + jp.put_back([&](int val) { vec.push_back(3); }, "test"); + jp.put_front([&](int val) { vec.push_back(4); }, "test"); + jp.put_front([&](int val) { vec.push_back(5); }, "test"); + jp.execute(0); + + ASSERT_EQ(vec, std::vector({5, 4, 1, 2, 3})); +} + +TEST_F(join_point_test, with_return_value) +{ + join_point jp("test"); + + std::vector vec; + std::string expected_str; + + jp.put_back([&](int, double, std::string) { vec.push_back(1); }, "test"); + jp.put_back([&](int, double, std::string) { vec.push_back(2); }, "test"); + jp.put_native([&](int b, double c, std::string str) -> double { + vec.push_back(3); + expected_str = str; + return c + b; + }); + double exec_res = jp.execute(5, 0.5, std::string("abc"), 0.0); + + ASSERT_EQ(vec, std::vector({3, 1, 2})); + ASSERT_EQ(exec_res, 5.5); + ASSERT_EQ(expected_str, "abc"); +} + +} // namespace dsn