diff --git a/score/mw/com/impl/methods/BUILD b/score/mw/com/impl/methods/BUILD index 13ba06da7..f00961999 100644 --- a/score/mw/com/impl/methods/BUILD +++ b/score/mw/com/impl/methods/BUILD @@ -28,6 +28,17 @@ cc_library( ], ) +cc_library( + name = "method_handler_checker", + srcs = ["method_handler_checker.cpp"], + hdrs = ["method_handler_checker.h"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com:__subpackages__", + ], +) + cc_library( name = "proxy_method_base", srcs = ["proxy_method_base.cpp"], @@ -111,6 +122,15 @@ cc_gtest_unit_test( ], ) +cc_gtest_unit_test( + name = "method_handler_checker_test", + srcs = ["method_handler_checker_test.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":method_handler_checker", + ], +) + cc_unit_test( name = "proxy_method_test", srcs = ["proxy_method_test.cpp"], @@ -155,6 +175,7 @@ cc_library( "//score/mw/com/impl:__subpackages__", ], deps = [ + ":method_handler_checker", ":skeleton_method_binding", "//score/mw/com/impl:instance_identifier", "//score/mw/com/impl:skeleton_base", @@ -188,6 +209,7 @@ cc_gtest_unit_test( ":skeleton_method", "//score/mw/com/impl/bindings/mock_binding", "//score/mw/com/impl/plumbing:skeleton_method_binding_factory_mock", + "//score/mw/com/impl/test:binding_factory_resources", "@score_baselibs//score/language/futurecpp", ], ) @@ -209,6 +231,7 @@ cc_gtest_unit_test( cc_unit_test_suites_for_host_and_qnx( name = "unit_test_suite", cc_unit_tests = [ + ":method_handler_checker_test", ":method_signature_element_ptr_test", ":proxy_method_test", "skeleton_method_base_test", diff --git a/score/mw/com/impl/methods/method_handler_checker.cpp b/score/mw/com/impl/methods/method_handler_checker.cpp new file mode 100644 index 000000000..35391fb98 --- /dev/null +++ b/score/mw/com/impl/methods/method_handler_checker.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/methods/method_handler_checker.h" diff --git a/score/mw/com/impl/methods/method_handler_checker.h b/score/mw/com/impl/methods/method_handler_checker.h new file mode 100644 index 000000000..3a237c43c --- /dev/null +++ b/score/mw/com/impl/methods/method_handler_checker.h @@ -0,0 +1,572 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_IMPL_METHODS_METHOD_HANDLER_CHECKER_H +#define SCORE_MW_COM_IMPL_METHODS_METHOD_HANDLER_CHECKER_H + +#include +#include + +namespace score::mw::com::impl +{ +namespace detail +{ + +template +struct get_method_in_arg_and_return_types : public get_method_in_arg_and_return_types +{ +}; + +template +struct get_method_in_arg_and_return_types +{ + using return_type = RetArg; + using in_args = std::tuple; +}; + +template +struct get_method_in_arg_types : public get_method_in_arg_types +{ +}; + +template +struct get_method_in_arg_types +{ + using type = std::tuple; +}; + +template +struct get_method_return_type : public get_method_return_type +{ +}; + +template +struct get_method_return_type +{ + using type = RetArg; +}; + +template +struct get_callable_return_type : public get_callable_return_type +{ +}; + +template +struct get_callable_return_type +{ + using type = Ret; +}; + +} // namespace detail + +template +constexpr void AssertCallableMatchesMethodSignature() +{ + using CallableReturnType = typename detail::get_callable_return_type::type; + static_assert(std::is_same_v, + "Registered method callable must have void return type! The actual method return type is passed as " + "first argument to the callable as non-const reference!"); + + constexpr bool has_in_args = (sizeof...(ArgTypes) != 0); + constexpr bool has_return_type = !std::is_same_v; + if constexpr (has_in_args && has_return_type) + { + using MethodInArgTuple = typename detail::get_method_in_arg_and_return_types::in_args; + using MethodReturnType = typename detail::get_method_in_arg_and_return_types::return_type; + + using ExpectedInArgTypeTuple = std::tuple; + using ExpectedReturnType = ReturnType&; + + static_assert( + std::is_same_v, + "Registered method callable must have the method return type as first argument as non-const reference!"); + static_assert(std::is_same_v, + "Registered method callable must have the same in argument types as the method signature " + "following the return type, but with const reference semantics!"); + } + else if constexpr (!has_in_args && has_return_type) + { + using MethodCallableReturnType = typename detail::get_method_return_type::type; + using ExpectedReturnType = ReturnType&; + + static_assert( + std::is_same_v, + "Registered method callable must have the method return type as only argument as non-const reference!"); + } + else if constexpr (has_in_args && !has_return_type) + { + using MethodCallableInArgTuple = typename detail::get_method_in_arg_types::type; + using ExpectedInArgTypeTuple = std::tuple; + + static_assert(std::is_same_v, + "Registered method callable must have only the in argument types from the method signature, but " + "with const reference semantics!"); + } + else + { + using MethodCallableInArgTuple = typename detail::get_method_in_arg_types::type; + static_assert(std::is_same_v>, + "Registered method callable must not have any arguments since the method signature does not " + "specify any in arguments or return type!"); + } +} + +} // namespace score::mw::com::impl + +#endif // SCORE_MW_COM_IMPL_METHODS_METHOD_HANDLER_CHECKER_H + +/** +#include +#include +#include +#include +#include +#include + +// template +// struct are_same; + +// template +// struct are_same +// { +// static const bool value = std::is_same_v && are_same::value; +// }; + +// template +// struct are_same +// { +// static const bool value = std::is_same_v; +// }; + + + +template +struct are_const_lvalue_references; + +template +struct are_const_lvalue_references +{ + static const bool value = ((std::is_lvalue_reference_v && std::is_const_v>) + && are_const_lvalue_references::value); +}; + +template +struct are_const_lvalue_references +{ + static const bool value = std::is_lvalue_reference_v && std::is_const_v>; +}; + +template +struct assert_callable_with_in_args_and_return : public +assert_callable_with_in_args_and_return {}; + +template +struct assert_callable_with_in_args_and_return { + static_assert(std::is_same_v); + static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + static_assert(are_const_lvalue_references::value); +}; + + +template +struct assert_callable_with_in_args_only : public assert_callable_with_in_args_only {}; + +template +struct assert_callable_with_in_args_only { + static_assert(std::is_same_v); + static_assert(are_const_lvalue_references::value); +}; + + + +template +struct assert_callable_with_return_only : public assert_callable_with_return_only {}; + +template +struct assert_callable_with_return_only { + static_assert(std::is_same_v); + static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + // static_assert(are_const_lvalue_references::value); +}; + + + + +template +struct assert_callable_without_in_args_or_return : public +assert_callable_without_in_args_or_return {}; + +template +struct assert_callable_without_in_args_or_return { + static_assert(std::is_same_v); + // static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + // static_assert(are_const_lvalue_references::value); +}; + + +// template +// struct generic_assert : public generic_assert {}; + +// template +// struct generic_assert { +// static_assert(std::is_same_v); +// static_assert(std::is_lvalue_reference_v && !std::is_const_v>); +// static_assert(are_const_lvalue_references::value); +// }; + + +template +struct get_method_in_arg_type_tuple : public get_method_in_arg_type_tuple {}; + +template +struct get_method_in_arg_type_tuple { + using type = std::tuple; +}; + + +template +struct get_method_return_type : public get_method_return_type {}; + +template +struct get_method_return_type { + using type = RetArg; +}; + + +template +class SkeletonMethod{ +public: + template + void func(Callable callable) + { + constexpr bool has_in_args = (sizeof...(ArgTypes) != 0); + constexpr bool has_return_type = !std::is_same_v; + + if constexpr(has_in_args) + { + // using ExpectedInArgsSignature = const ArgTypes&...; + // using ExpectedReturnSignature = ReturnType&; + + using CallableReturnType = typename get_method_return_type::type; + using CallableInArgsTuple = typename get_method_in_arg_type_tuple::type; + + using ExpectedInArgsTuple = std::tuple; + using ExpectedReturnType = std::tuple; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + else if constexpr(has_in_args && !has_return_type) + { + assert_callable_with_in_args_only{}; + } + else if constexpr(!has_in_args && has_return_type) + { + assert_callable_with_return_only{}; + } + else + { + assert_callable_without_in_args_or_return{}; + } + + } +}; + +int main() +{ + // SkeletonMethod method{}; + // method.func([](int& ret, const int& value, const double& value2){}); + + // using TupleType = get_method_in_arg_type_tuple>::type; + // int j{}; + // TupleType val{j, 2.0}; + + // std::tuple v{1}; + + SkeletonMethod method{}; + method.func([](int&, const int& value, const double& v2){}); + // method.func([](int&, const int& value){}); + + SkeletonMethod method2{}; + method2.func([](const int& value, const double& v2){}); + // method2.func([](const int& value){}); + + SkeletonMethod method3{}; + method3.func([](int&){}); + // // method3.func([](int&, const int&){}); + + SkeletonMethod method4{}; + method4.func([](){}); + // method4.func([](int){}); + + static_assert(std::is_const_v>); + static_assert(are_const_lvalue_references::value); + static_assert(are_const_lvalue_references::value); + // assertion>{}; + // assertion>{}; + + // option([](int){}); + // option(std::function{}); + return 0; +} + +**/ + +/** +#include +#include +#include +#include +#include +#include + +// template +// struct are_same; + +// template +// struct are_same +// { +// static const bool value = std::is_same_v && are_same::value; +// }; + +// template +// struct are_same +// { +// static const bool value = std::is_same_v; +// }; + + + +template +struct are_const_lvalue_references; + +template +struct are_const_lvalue_references +{ + static const bool value = ((std::is_lvalue_reference_v && std::is_const_v>) + && are_const_lvalue_references::value); +}; + +template +struct are_const_lvalue_references +{ + static const bool value = std::is_lvalue_reference_v && std::is_const_v>; +}; + +template +struct assert_callable_with_in_args_and_return : public +assert_callable_with_in_args_and_return {}; + +template +struct assert_callable_with_in_args_and_return { + static_assert(std::is_same_v); + static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + static_assert(are_const_lvalue_references::value); +}; + + +template +struct assert_callable_with_in_args_only : public assert_callable_with_in_args_only {}; + +template +struct assert_callable_with_in_args_only { + static_assert(std::is_same_v); + static_assert(are_const_lvalue_references::value); +}; + + + +template +struct assert_callable_with_return_only : public assert_callable_with_return_only {}; + +template +struct assert_callable_with_return_only { + static_assert(std::is_same_v); + static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + // static_assert(are_const_lvalue_references::value); +}; + + + + +template +struct assert_callable_without_in_args_or_return : public +assert_callable_without_in_args_or_return {}; + +template +struct assert_callable_without_in_args_or_return { + static_assert(std::is_same_v); + // static_assert(std::is_lvalue_reference_v && !std::is_const_v>); + // static_assert(are_const_lvalue_references::value); +}; + + +// template +// struct generic_assert : public generic_assert {}; + +// template +// struct generic_assert { +// static_assert(std::is_same_v); +// static_assert(std::is_lvalue_reference_v && !std::is_const_v>); +// static_assert(are_const_lvalue_references::value); +// }; + + +template +struct get_method_in_arg_and_return_types : public get_method_in_arg_and_return_types {}; + +template +struct get_method_in_arg_and_return_types { + using return_type = RetArg; + using in_args = std::tuple; +}; + + +template +struct get_method_in_arg_types : public get_method_in_arg_types {}; + +template +struct get_method_in_arg_types { + using type = std::tuple; +}; + + +template +struct get_method_return_type : public get_method_return_type {}; + +template +struct get_method_return_type { + using type = RetArg; +}; + + +template +struct get_callable_return_type : public get_callable_return_type {}; + +template +struct get_callable_return_type { + using type = Ret; +}; + + + +// template +// struct get_in_args_and_return +// { +// using return_type = Return; +// using in_args = std::tuple; +// }; + + + + +// template +// struct get_method_in_arg_type_tuple : public get_method_in_arg_type_tuple {}; + +// template +// struct get_method_in_arg_type_tuple { +// using type = std::tuple; +// }; + + +// template +// struct get_method_return_type : public get_method_return_type {}; + +// template +// struct get_method_return_type { +// using type = RetArg; +// }; + + +template +class SkeletonMethod{ +public: + template + void func(Callable callable) + { + constexpr bool has_in_args = (sizeof...(ArgTypes) != 0); + constexpr bool has_return_type = !std::is_same_v; + + using CallableReturnType = typename get_callable_return_type::type; + static_assert(std::is_same_v); + + if constexpr(has_in_args && has_return_type) + { + using MethodInArgTuple = typename get_method_in_arg_and_return_types::in_args; + using MethodReturnType = typename get_method_in_arg_and_return_types::return_type; + + using ExpectedInArgTypeTuple = std::tuple; + using ExpectedReturnType = ReturnType&; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + else if constexpr(!has_in_args && has_return_type) + { + using MethodCallableReturnType = typename get_method_return_type::type; + using ExpectedReturnType = ReturnType&; + + static_assert(std::is_same_v); + } + else if constexpr(has_in_args && !has_return_type) + { + using MethodCallableInArgTuple = typename get_method_in_arg_types::type; + using ExpectedInArgTypeTuple = std::tuple; + + static_assert(std::is_same_v); + } + else + { + using MethodCallableInArgTuple = typename get_method_in_arg_types::type; + static_assert(std::is_same_v>); + } + } +}; + +int main() +{ + // SkeletonMethod method{}; + // method.func([](int& ret, const int& value, const double& value2){}); + + // using TupleType = get_method_in_arg_type_tuple>::type; + // int j{}; + // TupleType val{j, 2.0}; + + // std::tuple v{1}; + + SkeletonMethod method{}; + method.func([](int&, const int&, const double&){}); + + SkeletonMethod method2{}; + method2.func([](const int& value, const double& v2){}); + // method2.func([](const int& value){}); + + SkeletonMethod method3{}; + method3.func([](int&){}); + // method3.func([](const int&){}); + + SkeletonMethod method4{}; + method4.func([](){}); + // method4.func([](int){}); + + static_assert(std::is_const_v>); + static_assert(are_const_lvalue_references::value); + static_assert(are_const_lvalue_references::value); + // assertion>{}; + // assertion>{}; + + // option([](int){}); + // option(std::function{}); + return 0; +} + +**/ diff --git a/score/mw/com/impl/methods/method_handler_checker_test.cpp b/score/mw/com/impl/methods/method_handler_checker_test.cpp new file mode 100644 index 000000000..63ff53877 --- /dev/null +++ b/score/mw/com/impl/methods/method_handler_checker_test.cpp @@ -0,0 +1,242 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/methods/method_handler_checker.h" + +#include + +#include + +namespace score::mw::com::impl +{ +namespace +{ + +TEST(MethodHandlerCheckerReturnAndInArgsTest, CallingAssertCallableWithValidCallableDoesNotTerminate) +{ + // Given a method with a return type and InArgs. + using MethodReturnType = int; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts the return and InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the call does not terminate + AssertCallableMatchesMethodSignature(); +} + +// TODO: Tests for invalid callables that should cause static assertion failures. These tests are disabled since we +// currently don't have infrastructure for compile time testing. To be enabled in SWP-46885. +#if 0 +TEST(MethodHandlerCheckerReturnAndInArgsTest, CallingAssertCallableWithCallableWithMissingReturnTerminates) +{ + // Given a method with a return type and InArgs. + using MethodReturnType = int; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts only the InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerReturnAndInArgsTest, CallingAssertCallableWithCallableWithMissingInArgTerminates) +{ + // Given a method with a return type and InArgs. + using MethodReturnType = int; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts only the return and only one of the InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerReturnAndInArgsTest, CallingAssertCallableWithCallableWithConstReturnTypeRefTerminates) +{ + // Given a method with a return type and InArgs. + using MethodReturnType = int; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts the correct arguments but the return type is a const reference instead of + // non-const reference. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerReturnAndInArgsTest, CallingAssertCallableWithCallableWithNonConstInArgRefTerminates) +{ + // Given a method with a return type and InArgs. + using MethodReturnType = int; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts the correct arguments but one of the InArgs is a non-const reference instead of + // a const reference. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} +#endif + +TEST(MethodHandlerCheckerReturnOnlyTest, CallingAssertCallableWithValidCallableDoesNotTerminate) +{ + // Given a method with a return type and no InArgs. + using MethodReturnType = int; + + // and given a callable that accepts the return and no InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the call does not terminate + AssertCallableMatchesMethodSignature(); +} + +// TODO: Tests for invalid callables that should cause static assertion failures. These tests are disabled since we +// currently don't have infrastructure for compile time testing. To be enabled in SWP-46885. +#if 0 +TEST(MethodHandlerCheckerReturnOnlyTest, CallingAssertCallableWithCallableWithMissingReturnTerminates) +{ + // Given a method with a return type and no InArgs. + using MethodReturnType = int; + + // and given a callable that accepts no return and no InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerReturnOnlyTest, CallingAssertCallableWithCallableWithConstReturnTypeRefTerminates) +{ + // Given a method with a return type and no InArgs. + using MethodReturnType = int; + + // and given a callable that accepts the return type as const reference instead of non-const reference. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} +#endif + +TEST(MethodHandlerCheckerInArgsOnlyTest, CallingAssertCallableWithValidCallableDoesNotTerminate) +{ + // Given a method with no return type and InArgs. + using MethodReturnType = void; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts no return and InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the call does not terminate + AssertCallableMatchesMethodSignature(); +} + +// TODO: Tests for invalid callables that should cause static assertion failures. These tests are disabled since we +// currently don't have infrastructure for compile time testing. To be enabled in SWP-46885. +#if 0 +TEST(MethodHandlerCheckerInArgsOnlyTest, CallingAssertCallableWithCallableWithMissingInArgTerminates) +{ + // Given a method with no return type and InArgs. + using MethodReturnType = void; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts only one InArg. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerInArgsOnlyTest, CallingAssertCallableWithCallableWithNonConstInArgRefTerminates) +{ + // Given a method with no return type and InArgs. + using MethodReturnType = void; + using FirstInArgType = int; + using SecondInArgType = double; + + // and given a callable that accepts one of the InArgs as non-const reference instead of const reference. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} +#endif + +TEST(MethodHandlerCheckerNoReturnAndNoInArgsTest, CallingAssertCallableWithValidCallableDoesNotTerminate) +{ + // Given a method with no return type and no InArgs. + using MethodReturnType = void; + + // and given a callable that accepts no return and no InArgs. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the call does not terminate + AssertCallableMatchesMethodSignature(); +} + +// TODO: Tests for invalid callables that should cause static assertion failures. These tests are disabled since we +// currently don't have infrastructure for compile time testing. To be enabled in SWP-46885. +#if 0 +TEST(MethodHandlerCheckerNoReturnAndNoInArgsTest, CallingAssertCallableWithCallableWithInArgTerminates) +{ + // Given a method with no return type and no InArgs. + using MethodReturnType = void; + + // and given a callable that accepts an InArg. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} + +TEST(MethodHandlerCheckerNoReturnAndNoInArgsTest, CallingAssertCallableWithCallableWithReturnTypeTerminates) +{ + // Given a method with no return type and no InArgs. + using MethodReturnType = void; + + // and given a callable that returns a non-void value. + using ValidCallable = std::function; + + // When calling AssertCallableMatchesMethodSignature + // Then the function should not compile + AssertCallableMatchesMethodSignature(); +} +#endif + +} // namespace + +} // namespace score::mw::com::impl diff --git a/score/mw/com/impl/methods/skeleton_method.h b/score/mw/com/impl/methods/skeleton_method.h index 8164f2761..5fb70d07f 100644 --- a/score/mw/com/impl/methods/skeleton_method.h +++ b/score/mw/com/impl/methods/skeleton_method.h @@ -12,7 +12,9 @@ ********************************************************************************/ #ifndef SCORE_MW_COM_IMPL_METHODS_SKELETON_METHOD_H #define SCORE_MW_COM_IMPL_METHODS_SKELETON_METHOD_H + #include "score/mw/com/impl/method_type.h" +#include "score/mw/com/impl/methods/method_handler_checker.h" #include "score/mw/com/impl/methods/skeleton_method_base.h" #include "score/mw/com/impl/methods/skeleton_method_binding.h" #include "score/mw/com/impl/plumbing/skeleton_method_binding_factory.h" @@ -21,7 +23,6 @@ #include "score/result/result.h" #include -#include #include #include #include @@ -160,19 +161,11 @@ Result SkeletonMethod::RegisterHandler(Callable&& { static_assert(std::is_rvalue_reference_v, "Callbeck provided to register has to be an rvalue reference"); - - // A move would only be problematic if the forwarding refference Callback&& evealuates to an LValue and and is moved - // from (which the caller of the function might not expect). We static assert that the callback is an RValue - // refference and can always be movef from. - // NOLINTNEXTLINE(bugprone-move-forwarding-reference) The callback is asserted to be an RValue reference - auto callable_invoker = [callable = std::move(callback)](auto&&... ptrs) -> decltype(auto) { - return std::invoke(callable, (*ptrs)...); - }; + AssertCallableMatchesMethodSignature(); SkeletonMethodBinding::TypeErasedHandler type_erased_callable = - [callable_invoker = std::move(callable_invoker)]( - std::optional> type_erased_in_args, - std::optional> type_erased_return) { + [callback = std::move(callback)](std::optional> type_erased_in_args, + std::optional> type_erased_return) { using InArgPtrTuple = std::tuple; InArgPtrTuple typed_in_arg_ptrs{}; @@ -189,15 +182,29 @@ Result SkeletonMethod::RegisterHandler(Callable&& constexpr bool is_return_type_not_void = !std::is_same_v; if constexpr (is_return_type_not_void) { + const auto typed_return_ptr_tuple = Deserialize(type_erased_return.value()); + auto* const typed_return_ptr = std::get<0>(typed_return_ptr_tuple); SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE( type_erased_return.has_value(), "ReturnType is non void. Thus, type_erased_result needs to have a value!"); - ReturnType res = std::apply(callable_invoker, std::forward(typed_in_arg_ptrs)); - SerializeArgs(type_erased_return.value(), res); + + // Call the callable with the typed_return_ptr and the typed_in_arg_ptrs which are unpacked from the + // tuple into individual arguments. + std::apply( + [&callback, typed_return_ptr](ArgTypes*... typed_in_arg_ptrs) { + std::invoke(callback, *typed_return_ptr, *typed_in_arg_ptrs...); + }, + typed_in_arg_ptrs); } else { - std::apply(callable_invoker, std::forward(typed_in_arg_ptrs)); + // Call the callable with the typed_in_arg_ptrs which are unpacked from the tuple into individual + // arguments. + std::apply( + [&callback](ArgTypes*... typed_in_arg_ptrs) { + std::invoke(callback, *typed_in_arg_ptrs...); + }, + typed_in_arg_ptrs); } }; @@ -205,4 +212,5 @@ Result SkeletonMethod::RegisterHandler(Callable&& } } // namespace score::mw::com::impl + #endif // SCORE_MW_COM_IMPL_METHODS_SKELETON_METHOD_H diff --git a/score/mw/com/impl/methods/skeleton_method_test.cpp b/score/mw/com/impl/methods/skeleton_method_test.cpp index 5173ab4f8..4b105caa1 100644 --- a/score/mw/com/impl/methods/skeleton_method_test.cpp +++ b/score/mw/com/impl/methods/skeleton_method_test.cpp @@ -12,6 +12,7 @@ ********************************************************************************/ #include "score/mw/com/impl/methods/skeleton_method.h" +#include "gmock/gmock.h" #include "score/mw/com/impl/bindings/mock_binding/skeleton.h" #include "score/mw/com/impl/bindings/mock_binding/skeleton_method.h" #include "score/mw/com/impl/instance_identifier.h" @@ -20,6 +21,7 @@ #include "score/mw/com/impl/plumbing/skeleton_method_binding_factory.h" #include "score/mw/com/impl/plumbing/skeleton_method_binding_factory_mock.h" #include "score/mw/com/impl/skeleton_base.h" +#include "score/mw/com/impl/test/binding_factory_resources.h" #include "score/result/result.h" #include @@ -56,22 +58,7 @@ class EmptySkeleton final : public SkeletonBase }; using TestMethodType = bool(int, bool); - -class SkeletonMethodTestFixture : public ::testing::Test -{ - public: - void CreateSkeletonMethod() - { - EmptySkeleton empty_skeleton{std::make_unique(), kInstanceIdWithLolaBinding}; - auto mock_method_binding_ptr = std::make_unique(mock_method_binding_); - - method_ = std::make_unique>( - empty_skeleton, "dummy_method", std::move(mock_method_binding_ptr)); - } - - std::unique_ptr> method_{nullptr}; - mock_binding::SkeletonMethod mock_method_binding_{}; -}; +using TestMethodHandlerType = void(bool&, const int&, const bool&); TEST(SkeletonMethodTests, NotCopyable) { @@ -99,17 +86,39 @@ TEST(SkeletonMethodTest, ClassTypeDependsOnMethodType) "Class type does not depend on event data type"); } -template +template class SkeletonMethodTypedTest : public ::testing::Test { public: - using Type = T; + using MethodType = typename Types::MethodType; + using HandlerType = typename Types::HandlerType; void SetUp() override { - ON_CALL(skeleton_method_binding_mock_, RegisterHandler(_)).WillByDefault(Return(Result{})); + ON_CALL(mock_method_binding_, RegisterHandler(_)).WillByDefault(Return(Result{})); } - mock_binding::SkeletonMethod skeleton_method_binding_mock_; + + SkeletonMethodTypedTest& GivenASkeletonMethod() + { + auto mock_method_binding_ptr = std::make_unique(mock_method_binding_); + + method_ = std::make_unique>( + empty_skeleton_, method_name_, std::move(mock_method_binding_ptr)); + return *this; + } + + SkeletonMethodTypedTest& WithAMockedMethodBindingfactory() + { + skeleton_method_binding_factory_mock_guard_ = std::make_unique(); + return *this; + } + + EmptySkeleton empty_skeleton_{std::make_unique(), kInstanceIdWithLolaBinding}; + std::string method_name_{"dummy_method"}; + std::unique_ptr> method_{nullptr}; + mock_binding::SkeletonMethod mock_method_binding_{}; + + std::unique_ptr skeleton_method_binding_factory_mock_guard_{nullptr}; }; struct MyDataStruct @@ -119,67 +128,60 @@ struct MyDataStruct double d; float f[4]; }; -using RegisteredFunctionTypes = ::testing::Types; + +template +struct MethodTypeAndHandlerType +{ + using MethodType = MethodTypeIn; + using HandlerType = HandlerTypeIn; +}; +using RegisteredFunctionTypes = + ::testing::Types, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType, + MethodTypeAndHandlerType>; TYPED_TEST_SUITE(SkeletonMethodTypedTest, RegisteredFunctionTypes, ); +using SkeletonMethodTestFixture = + SkeletonMethodTypedTest>; + TYPED_TEST(SkeletonMethodTypedTest, AnyCombinationOfReturnAndInputArgTypesCanBeRegistered) { - // Given A skeleton Method with a mock method binding - EmptySkeleton empty_skeleton{std::make_unique(), kInstanceIdWithLolaBinding}; - auto mock_method_binding_ptr = std::make_unique(); - auto& mock_method_binding = *mock_method_binding_ptr; - - // And a method type that can be one of the four qualitatively different Types - // void() SomeType() - // void(SomeTypes) SomeType(SomeTypes) - using FixtureMethodType = typename TestFixture::Type; - SkeletonMethod method{empty_skeleton, "dummy_method", std::move(mock_method_binding_ptr)}; + this->GivenASkeletonMethod(); // Expecting that the register call is dispatched to the binding without errors - EXPECT_CALL(mock_method_binding, RegisterHandler(_)); + EXPECT_CALL(this->mock_method_binding_, RegisterHandler(_)); - // When a Register call is issued at the binding independent level - score::cpp::callback test_callback{}; - - std::ignore = method.RegisterHandler(std::move(test_callback)); + // // When a Register call is issued at the binding independent level + using HandlerType = typename TestFixture::HandlerType; + score::cpp::callback test_callback{}; + std::ignore = this->method_->RegisterHandler(std::move(test_callback)); } TYPED_TEST(SkeletonMethodTypedTest, TwoParameterConstructorCorrectlyCallsBindingFactoryAndSkeletonMethodIsCreated) { + this->WithAMockedMethodBindingfactory(); - auto skeleton_method_binding_factory_mock = SkeletonMethodBindingFactoryMock(); - SkeletonMethodBindingFactory::InjectMockBinding(&skeleton_method_binding_factory_mock); - - auto skeleton_method_binding = - std::make_unique(this->skeleton_method_binding_mock_); - - // Given A skeleton Method with a mock method binding - - EmptySkeleton empty_skeleton{std::make_unique(), kInstanceIdWithLolaBinding}; - - // And a method type that can be one of the four qualitatively different Types void() SomeType() - // void(SomeTypes) SomeType(SomeTypes) - - // expecting that a binding factory cannot crete a binding - EXPECT_CALL(skeleton_method_binding_factory_mock, - Create(_ /*handle*/, _ /*parent binding*/, _ /*method_name*/, _ /*method_type*/)) - .WillOnce(testing::Return(testing::ByMove(std::move(skeleton_method_binding)))); + // Expecting that a binding factory can create a binding + EXPECT_CALL(this->skeleton_method_binding_factory_mock_guard_->factory_mock_, + Create(_ /*handle*/, _ /*parent binding*/, this->method_name_, _)) + .WillOnce(testing::Return( + testing::ByMove(std::make_unique(this->mock_method_binding_)))); // When the 2-parameter constructor of the SkeletonMethod class is called - using FixtureMethodType = typename TestFixture::Type; - SkeletonMethod method{empty_skeleton, "dummy_method"}; - - EXPECT_CALL(this->skeleton_method_binding_mock_, RegisterHandler(_)); + using FixtureMethodType = typename TestFixture::MethodType; + SkeletonMethod method{this->empty_skeleton_, this->method_name_}; // Then a Binding can be created which is capable of registering a callback - score::cpp::callback test_callback{}; + using HandlerType = typename TestFixture::HandlerType; + score::cpp::callback test_callback{}; EXPECT_TRUE(method.RegisterHandler(std::move(test_callback))); } @@ -187,45 +189,31 @@ TYPED_TEST( SkeletonMethodTypedTest, TwoParameterConstructorCorrectlyCallsBindingFactoryButSkeletonMethodIsNotCreatedWhenTheBindingFactoryDoesNotReturnBinding) { + this->WithAMockedMethodBindingfactory(); - auto skeleton_method_binding_factory_mock = SkeletonMethodBindingFactoryMock(); - SkeletonMethodBindingFactory::InjectMockBinding(&skeleton_method_binding_factory_mock); - - auto skeleton_method_binding = - std::make_unique(this->skeleton_method_binding_mock_); - - // Given A skeleton Method with a mock method binding - - EmptySkeleton empty_skeleton{std::make_unique(), kInstanceIdWithLolaBinding}; - - // And a method type that can be one of the four qualitatively different Types - // void() SomeType() - // void(SomeTypes) SomeType(SomeTypes) - - // expecting that a binding factory cannot crete a binding - EXPECT_CALL(skeleton_method_binding_factory_mock, - Create(_ /*handle*/, _ /*parent binding*/, _ /*method_name*/, _ /*method_type*/)) + // Expecting that a binding factory cannot create a binding + EXPECT_CALL(this->skeleton_method_binding_factory_mock_guard_->factory_mock_, + Create(_ /*handle*/, _ /*parent binding*/, this->method_name_, _ /*method_type*/)) .WillOnce(testing::Return(testing::ByMove(nullptr))); // When the 2-parameter constructor of the SkeletonMethod class is called - using FixtureMethodType = typename TestFixture::Type; - SkeletonMethod method{empty_skeleton, "dummy_method"}; + using FixtureMethodType = typename TestFixture::MethodType; + SkeletonMethod method{this->empty_skeleton_, this->method_name_}; // Then the binding cannot be created and calling AreBindingsValid returns false - EXPECT_FALSE(SkeletonBaseView{empty_skeleton}.AreBindingsValid()); + EXPECT_FALSE(SkeletonBaseView{this->empty_skeleton_}.AreBindingsValid()); } TEST_F(SkeletonMethodTestFixture, ACallbackWithAPointerAsStateCanBeRegistered) { - // Given A skeleton Method with a mock method binding - CreateSkeletonMethod(); + GivenASkeletonMethod(); // And a callback with a unique_ptr as a state auto test_struct_p = std::make_unique(); - score::cpp::callback test_callback_with_state = [state = std::move(test_struct_p)]( - int, bool b) noexcept { - return state->b || b; - }; + score::cpp::callback test_callback_with_state = + [state = std::move(test_struct_p)](bool& return_value, const int&, const bool& b) noexcept { + return_value = state->b || b; + }; // Expecting that the register call is dispatched to the binding without an error EXPECT_CALL(mock_method_binding_, RegisterHandler(_)); @@ -237,34 +225,38 @@ TEST_F(SkeletonMethodTestFixture, ACallbackWithAPointerAsStateCanBeRegistered) using Thing = long; using InType1 = double; using InType2 = int; -using VoidVoid = void(); -using ThingVoid = Thing(); -using VoidStuff = void(InType1, InType2); -using ThingStuff = Thing(InType1, InType2); - -template +using VoidVoid = MethodTypeAndHandlerType; +using ThingVoid = MethodTypeAndHandlerType; +using VoidStuff = + MethodTypeAndHandlerType; +using ThingStuff = MethodTypeAndHandlerType; + +template class SkeletonMethodGenericTestFixture : public ::testing::Test { - public: void CreateSkeletonMethodWithMockedTypeErasedCallback() { EmptySkeleton empty_skeleton{std::make_unique(), kInstanceIdWithLolaBinding}; auto mock_method_binding_ptr = std::make_unique(mock_method_binding_); - method_ = std::make_unique>( + method_ = std::make_unique>( empty_skeleton, "dummy_method", std::move(mock_method_binding_ptr)); } static constexpr std::size_t in_args_buffer_size = sizeof(InType1) + sizeof(InType2); - void SerializeBuffers(InType1 in_arg_1, InType2 in_arg_2) + void SerializeBuffers(InType1 in_arg_1, InType2 in_arg_2, InType2 in_arg_3) { constexpr std::size_t in_type_1_size = sizeof(InType1); + constexpr std::size_t in_type_2_size = sizeof(InType2); std::byte* write_head = in_args_buffer_.begin(); new (write_head) InType1(in_arg_1); write_head += in_type_1_size; new (write_head) InType2(in_arg_2); + write_head += in_type_2_size; + new (write_head) InType2(in_arg_3); } Thing GetTypedResultFromOutArgBuffer() { @@ -273,8 +265,8 @@ class SkeletonMethodGenericTestFixture : public ::testing::Test std::array out_arg_buffer_{}; std::array in_args_buffer_{}; - std::unique_ptr> method_{nullptr}; - ::testing::MockFunction typed_callback_mock_{}; + std::unique_ptr> method_{nullptr}; + ::testing::MockFunction typed_callback_mock_{}; std::optional typeerased_callback_{}; mock_binding::SkeletonMethod mock_method_binding_{}; }; @@ -295,11 +287,15 @@ TEST_F(SkeletonMethodThingStuffFixture, DataTransferBetweenTypedAndTypeErasedCal Thing ret_val{505}; InType1 in_arg_1{6.12}; InType2 in_arg_2{17}; + InType2 in_arg_3{18}; - // Expecting that a typed callable will be called with correctly deserialized inargs and will return a value - EXPECT_CALL(typed_callback_mock_, Call(in_arg_1, in_arg_2)).WillOnce(Return(ret_val)); + // Expecting that a typed callable will be called with correctly deserialized inargs and return value + EXPECT_CALL(typed_callback_mock_, Call(_, in_arg_1, in_arg_2, in_arg_3)) + .WillOnce(Invoke([ret_val](auto& return_arg, auto, auto, auto) { + return_arg = ret_val; + })); - SerializeBuffers(in_arg_1, in_arg_2); + SerializeBuffers(in_arg_1, in_arg_2, in_arg_3); EXPECT_TRUE(method_->RegisterHandler(typed_callback_mock_.AsStdFunction())); // When the type erased call is executed by the binding typeerased_callback_.value()(in_args_buffer_, out_arg_buffer_); @@ -324,8 +320,10 @@ TEST_F(SkeletonMethodThingVoidFixture, DataTransferBetweenTypedAndTypeErasedCall Thing ret_val{50255}; - // Expecting that a typed callable will be called without inargs and will return a value - EXPECT_CALL(typed_callback_mock_, Call()).WillOnce(Return(ret_val)); + // Expecting that a typed callable will be called without inargs and return a value + EXPECT_CALL(typed_callback_mock_, Call(_)).WillOnce(Invoke([ret_val](auto& return_arg) { + return_arg = ret_val; + })); EXPECT_TRUE(method_->RegisterHandler(typed_callback_mock_.AsStdFunction())); @@ -352,11 +350,12 @@ TEST_F(SkeletonMethodVoidStuffFixture, DataTransferBetweenTypedAndTypeErasedCall InType1 in_arg_1{0.6}; InType2 in_arg_2{0x1700}; + InType2 in_arg_3{0x1701}; - // Expecting that a typed callable will be called with correctly deserialized inargs and will not return a value - EXPECT_CALL(typed_callback_mock_, Call(in_arg_1, in_arg_2)).WillOnce(Return()); + // Expecting that a typed callable will be called with correctly deserialized inargs and no return value + EXPECT_CALL(typed_callback_mock_, Call(in_arg_1, in_arg_2, in_arg_3)); - SerializeBuffers(in_arg_1, in_arg_2); + SerializeBuffers(in_arg_1, in_arg_2, in_arg_3); EXPECT_TRUE(method_->RegisterHandler(typed_callback_mock_.AsStdFunction())); // When the type erased call is executed by the binding @@ -376,13 +375,14 @@ TEST_F(SkeletonMethodVoidVoidFixture, DataTransferBetweenTypedAndTypeErasedCallb return {}; })); - // Expecting that a typed callable will be called without inargs and will not return a value - EXPECT_CALL(typed_callback_mock_, Call()).WillOnce(Return()); + // Expecting that a typed callable will be called without inargs or return value + EXPECT_CALL(typed_callback_mock_, Call()); EXPECT_TRUE(method_->RegisterHandler(typed_callback_mock_.AsStdFunction())); // When the type erased call is executed by the binding typeerased_callback_.value()({}, {}); } + } // namespace } // namespace score::mw::com::impl diff --git a/score/mw/com/impl/proxy_field.h b/score/mw/com/impl/proxy_field.h index 6019f55b8..97ce0a7d2 100644 --- a/score/mw/com/impl/proxy_field.h +++ b/score/mw/com/impl/proxy_field.h @@ -261,7 +261,7 @@ class ProxyField final : public ProxyFieldBase template ::value>> - score::Result> Set(SampleDataType& new_field_value) noexcept + score::Result> Set(const SampleDataType& new_field_value) noexcept { return proxy_method_set_dispatch_->operator()(new_field_value); } diff --git a/score/mw/com/impl/skeleton_base_test.cpp b/score/mw/com/impl/skeleton_base_test.cpp index 5f2138b2b..377d63ee0 100644 --- a/score/mw/com/impl/skeleton_base_test.cpp +++ b/score/mw/com/impl/skeleton_base_test.cpp @@ -659,6 +659,9 @@ class DummyField : public SkeletonFieldBase { public: using SkeletonFieldBase::SkeletonFieldBase; + + void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override {} + bool IsInitialValueSaved() const noexcept override { return false; @@ -668,7 +671,7 @@ class DummyField : public SkeletonFieldBase return Result{}; }; - bool IsSetHandlerRegistered() const noexcept override + bool IsSetHandlerMissing() const noexcept override { return false; } diff --git a/score/mw/com/impl/skeleton_field.h b/score/mw/com/impl/skeleton_field.h index 228ab650d..f045841de 100644 --- a/score/mw/com/impl/skeleton_field.h +++ b/score/mw/com/impl/skeleton_field.h @@ -14,6 +14,7 @@ #define SCORE_MW_COM_IMPL_SKELETON_FIELD_H #include "score/mw/com/impl/method_type.h" +#include "score/mw/com/impl/methods/method_handler_checker.h" #include "score/mw/com/impl/methods/skeleton_method.h" #include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h" #include "score/mw/com/impl/plumbing/skeleton_field_binding_factory.h" @@ -118,35 +119,68 @@ class SkeletonField : public SkeletonFieldBase // // \tparam CallableType Any callable (std::function, score::cpp::callback, lambda, ...) with the signature: // void(FieldType& new_value) - // - new_value : the value requested by the proxy. + // - new_value : the value requested by the proxy. This value will be modified in place by the registered handler + // and the new value will be used to update the field. template ::type = 0, typename CallableType> - Result RegisterSetHandler(CallableType&& handler) + Result RegisterSetHandler(CallableType&& set_handler) { - static_assert(std::is_invocable_v, + static_assert(std::is_invocable_r_v, "RegisterSetHandler: handler must be callable as void(FieldType& value). " "The argument initially holds the proxy-requested value and may be modified in-place."); - set_handler_ = std::move(handler); - auto wrapped_callback = [this](FieldType& new_value) -> FieldType { + auto wrapped_callback = [this, set_handler = std::move(set_handler)](FieldType& final_value, + const FieldType& desired_value) { + // Copy desired_value (which is a method InArg) into final_value (which is the method return value). + // final_value can then be modified in place by set_handler. + final_value = desired_value; + // Allow user to validate/modify the value in-place - set_handler_(new_value); + set_handler(final_value); - // Store the (possibly modified) value as the latest field value - auto update_result = this->Update(new_value); + // Copy the (possibly modified) value into the latest field value + auto update_result = this->Update(final_value); if (!update_result.has_value()) { score::mw::log::LogError("lola") << "Set handler: failed to update field value."; } - - // Return the accepted value to the proxy - return new_value; }; is_set_handler_registered_ = true; - return set_method_.get()->RegisterHandler(std::move(wrapped_callback)); + return set_method_->RegisterHandler(std::move(wrapped_callback)); + } + + /// \brief Updates the reference to SkeletonBase held by this SkeletonField and also the owned methods. + /// + /// This is necessary when a Skeleton (which owns its events, fields and methods) is moved to a new address. When + /// this happens, the references to the SkeletonBase are pointing to the old address and must be updated. This must + /// be done also for the get and set method since they call a SkeletonMethod constructor which does not register + /// them with the SkeletonBase. Rather, they're considered as part of the SkeletonField and it's the field's + /// responsibility to update their SkeletonBase reference when it's moved. + void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override + { + skeleton_base_ = skeleton_base; + + if (set_method_ != nullptr) + { + set_method_->UpdateSkeletonReference(skeleton_base); + } + if (get_method_ != nullptr) + { + get_method_->UpdateSkeletonReference(skeleton_base); + } } private: + using SetMethodSignature = FieldType(FieldType); + using GetMethodSignature = FieldType(); + + /// \brief Private delegating constructor used by the no-setter public ctor and testing ctor. + SkeletonField(SkeletonBase& parent, + std::unique_ptr> skeleton_event_dispatch, + std::unique_ptr> skeleton_set_method_dispatch, + std::unique_ptr> skeleton_get_method_dispatch, + const std::string_view field_name); + bool IsInitialValueSaved() const noexcept override { return initial_field_value_ != nullptr; @@ -159,59 +193,23 @@ class SkeletonField : public SkeletonFieldBase SkeletonEvent* GetTypedEvent() const noexcept; - std::unique_ptr initial_field_value_; - ISkeletonField* skeleton_field_mock_; - - // Zero-cost conditional storage: unique_ptr when EnableSet=true, zero-size tag when false. - using SetMethodSignature = FieldType(FieldType); - using SetMethodType = - std::conditional_t>, detail::EnableSetOnlyTag>; - SetMethodType set_method_; - - // Stores the user-provided set handler. Kept as a member so that the wrapped - // callback can invoke it via this->set_handler_. The concrete storage type is - // score::cpp::callback with the expected signature so that any callable provided - // to RegisterSetHandler is type-erased here. - // Zero-cost when EnableSet=false. - using SetHandlerStorageType = - std::conditional_t, detail::EnableSetOnlyTag>; - SetHandlerStorageType set_handler_{}; - - // Tracks whether RegisterSetHandler() has been called. Zero-cost when EnableSet=false. - using IsSetHandlerRegisteredType = std::conditional_t; - IsSetHandlerRegisteredType is_set_handler_registered_{}; - - // EnableSet=true: checks the flag; EnableSet=false: no setter, no handler required. - bool IsSetHandlerRegistered() const noexcept override + bool IsSetHandlerMissing() const noexcept override { - if constexpr (EnableSet) + if constexpr (!EnableSet) { - return is_set_handler_registered_; + return false; } - return true; + return !is_set_handler_registered_; } - /// \brief Private delegating constructor used by the setter-enabled public ctor. - template > - SkeletonField(SkeletonBase& parent, - std::unique_ptr> skeleton_event_dispatch, - const std::string_view field_name, - detail::EnableSetOnlyTag); + std::unique_ptr initial_field_value_; + ISkeletonField* skeleton_field_mock_; - /// \brief Private delegating constructor used by the no-setter public ctor and testing ctor. - SkeletonField(SkeletonBase& parent, - std::unique_ptr> skeleton_event_dispatch, - const std::string_view field_name); + // Tracks whether RegisterSetHandler() has been called. + bool is_set_handler_registered_; - // TODO: Move get_method_ initialization into the delegating constructors (like set_method_) once the - // Get handler is implemented. - using GetMethodSignature = FieldType(); - std::unique_ptr> get_method_{ - std::make_unique>( - skeleton_base_.get(), - field_name_, - ::score::mw::com::impl::MethodType::kGet, - typename SkeletonMethod::FieldOnlyConstructorEnabler{})}; + std::unique_ptr> set_method_; + std::unique_ptr> get_method_; }; /// \brief Public ctor — EnableSet=true: delegates to the private ctor that also creates the set method. @@ -229,8 +227,19 @@ SkeletonField::SkeletonField(Skeleton parent, field_name), typename SkeletonEvent::FieldOnlyConstructorEnabler{}), - field_name, - detail::EnableSetOnlyTag{}} + std::make_unique>( + parent, + field_name, + ::score::mw::com::impl::MethodType::kSet, + typename SkeletonMethod::FieldOnlyConstructorEnabler{}), + // TODO: Move get_method_ initialization into the delegating constructors (like set_method_) once the + // Get handler is implemented. + std::make_unique>( + parent, + field_name, + ::score::mw::com::impl::MethodType::kGet, + typename SkeletonMethod::FieldOnlyConstructorEnabler{}), + field_name} { } @@ -249,6 +258,8 @@ SkeletonField::SkeletonField(Skeleton parent, field_name), typename SkeletonEvent::FieldOnlyConstructorEnabler{}), + nullptr, + nullptr, field_name} { } @@ -261,40 +272,25 @@ SkeletonField::SkeletonField( std::unique_ptr> binding) : SkeletonField{skeleton_base, std::make_unique>(skeleton_base, field_name, std::move(binding)), + nullptr, + nullptr, field_name} { } -/// \brief Private delegating ctor — setter enabled. -template -template -SkeletonField::SkeletonField( - SkeletonBase& parent, - std::unique_ptr> skeleton_event_dispatch, - const std::string_view field_name, - detail::EnableSetOnlyTag) - : SkeletonFieldBase{parent, field_name, std::move(skeleton_event_dispatch)}, - initial_field_value_{nullptr}, - skeleton_field_mock_{nullptr} -{ - set_method_ = std::make_unique>( - parent, - field_name_, - ::score::mw::com::impl::MethodType::kSet, - typename SkeletonMethod::FieldOnlyConstructorEnabler{}); - SkeletonBaseView skeleton_base_view{parent}; - skeleton_base_view.RegisterField(field_name, *this); -} - -/// \brief Private delegating ctor — no setter. Receives the already-constructed event. template SkeletonField::SkeletonField( SkeletonBase& parent, std::unique_ptr> skeleton_event_dispatch, + std::unique_ptr> skeleton_set_method_dispatch, + std::unique_ptr> skeleton_get_method_dispatch, const std::string_view field_name) : SkeletonFieldBase{parent, field_name, std::move(skeleton_event_dispatch)}, initial_field_value_{nullptr}, - skeleton_field_mock_{nullptr} + skeleton_field_mock_{nullptr}, + is_set_handler_registered_{false}, + set_method_{std::move(skeleton_set_method_dispatch)}, + get_method_{std::move(skeleton_get_method_dispatch)} { SkeletonBaseView skeleton_base_view{parent}; skeleton_base_view.RegisterField(field_name, *this); @@ -310,9 +306,8 @@ SkeletonField::SkeletonField(Skeleton // coverity[autosar_cpp14_a12_8_3_violation] This is a false-positive. initial_field_value_{std::move(other.initial_field_value_)}, skeleton_field_mock_{other.skeleton_field_mock_}, - set_method_{std::move(other.set_method_)}, - set_handler_{std::move(other.set_handler_)}, is_set_handler_registered_{std::move(other.is_set_handler_registered_)}, + set_method_{std::move(other.set_method_)}, get_method_{std::move(other.get_method_)} { SkeletonBaseView skeleton_base_view{skeleton_base_.get()}; @@ -329,9 +324,8 @@ auto SkeletonField::operator=(Skeleto initial_field_value_ = std::move(other.initial_field_value_); skeleton_field_mock_ = std::move(other.skeleton_field_mock_); - set_method_ = std::move(other.set_method_); - set_handler_ = std::move(other.set_handler_); is_set_handler_registered_ = std::move(other.is_set_handler_registered_); + set_method_ = std::move(other.set_method_); get_method_ = std::move(other.get_method_); SkeletonBaseView skeleton_base_view{skeleton_base_.get()}; skeleton_base_view.UpdateField(field_name_, *this); diff --git a/score/mw/com/impl/skeleton_field_base.h b/score/mw/com/impl/skeleton_field_base.h index 0e0097b11..efe640931 100644 --- a/score/mw/com/impl/skeleton_field_base.h +++ b/score/mw/com/impl/skeleton_field_base.h @@ -50,10 +50,11 @@ class SkeletonFieldBase virtual ~SkeletonFieldBase() = default; - void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept - { - skeleton_base_ = skeleton_base; - } + /// \brief Updates the reference to SkeletonBase held by the SkeletonField and also the owned methods. + /// + /// This must happen in the derived class since the derived class owns the methods (this is required since they are + /// templated with the FieldType, which SkeletonFieldBase doesn't know). + virtual void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept = 0; /// \brief Used to indicate that the field shall be available to consumer (e.g. binding specific preparation) Result PrepareOffer() noexcept @@ -63,8 +64,8 @@ class SkeletonFieldBase if (!was_prepare_offer_called_) { // If the field is configured with a setter, the application must register - // a set handler before calling OfferService(), otherwise Offer() shall fail. - if (!IsSetHandlerRegistered()) + // a set handler via RegisterSetHandler before calling OfferService(), otherwise Offer() shall fail. + if (IsSetHandlerMissing()) { score::mw::log::LogWarn("lola") << "Set handler must be registered before offering field: " << field_name_; @@ -131,8 +132,9 @@ class SkeletonFieldBase /// \brief Returns whether the initial value has been saved by the user to be used by DoDeferredUpdate virtual bool IsInitialValueSaved() const noexcept = 0; - /// \brief Returns whether a set handler has been registered. - virtual bool IsSetHandlerRegistered() const noexcept = 0; + /// \brief Returns true if a setter has been enabled in the interface and a set handler was not registered via + /// RegisterSetHandler. Otherwise, returns false. + virtual bool IsSetHandlerMissing() const noexcept = 0; /// \brief Sets the initial value of the field. /// diff --git a/score/mw/com/impl/skeleton_field_base_test.cpp b/score/mw/com/impl/skeleton_field_base_test.cpp index c43a501cb..a141d7e36 100644 --- a/score/mw/com/impl/skeleton_field_base_test.cpp +++ b/score/mw/com/impl/skeleton_field_base_test.cpp @@ -64,6 +64,8 @@ class MyDummyField : public SkeletonFieldBase { } + void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override {} + StrictMock* GetMockEventBinding() noexcept { auto* const skeleton_field_base_binding = SkeletonFieldBaseView{*this}.GetEventBinding(); @@ -83,9 +85,9 @@ class MyDummyField : public SkeletonFieldBase return {}; } - bool IsSetHandlerRegistered() const noexcept override + bool IsSetHandlerMissing() const noexcept override { - return true; + return false; } bool was_deferred_update_called_{false}; @@ -95,14 +97,16 @@ class MyDummyField : public SkeletonFieldBase class MyDummyFieldFailingDeferredUpdate final : public MyDummyField { public: + void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override {} + Result DoDeferredUpdate() noexcept override { return MakeUnexpected(ComErrc::kCommunicationLinkError); } - bool IsSetHandlerRegistered() const noexcept override + bool IsSetHandlerMissing() const noexcept override { - return true; + return false; } }; diff --git a/score/mw/com/impl/skeleton_field_test.cpp b/score/mw/com/impl/skeleton_field_test.cpp index 1967b65c0..c62242ac3 100644 --- a/score/mw/com/impl/skeleton_field_test.cpp +++ b/score/mw/com/impl/skeleton_field_test.cpp @@ -12,6 +12,7 @@ ********************************************************************************/ #include "score/mw/com/impl/skeleton_field.h" +#include "method_type.h" #include "score/mw/com/impl/bindings/mock_binding/skeleton_method.h" #include "score/mw/com/impl/method_type.h" #include "score/mw/com/impl/methods/skeleton_method.h" @@ -48,6 +49,7 @@ using ::testing::StrictMock; using ::testing::WithArg; constexpr std::string_view kFieldName{"Field1"}; +const TestSampleType kDummyInitialValue{42}; ServiceIdentifierType kServiceIdentifier{make_ServiceIdentifierType("foo", 1U, 0U)}; std::uint16_t kInstanceId{23U}; @@ -68,6 +70,64 @@ class MyDummySkeleton : public SkeletonBase SkeletonField my_dummy_field_{*this, kFieldName}; }; +class SkeletonFieldTestFixture : public ::testing::Test +{ + protected: + void SetUp() override + { + ON_CALL(skeleton_field_binding_factory_mock_guard_.factory_mock_, + CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) + .WillByDefault(InvokeWithoutArgs([this]() { + return std::make_unique>( + skeleton_field_binding_mock_); + })); + + ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) + .WillByDefault(InvokeWithoutArgs([this]() { + return std::make_unique(skeleton_field_get_binding_mock_); + })); + + ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) + .WillByDefault(InvokeWithoutArgs([this]() { + return std::make_unique(skeleton_field_set_binding_mock_); + })); + + ON_CALL(skeleton_field_set_binding_mock_, RegisterHandler(_)).WillByDefault(Return(Result{})); + } + + /// \brief Returns a span pointing to storage containing the provided field value + std::pair, score::cpp::span> CreateFieldSetterInArgAndReturnSpans( + const TestSampleType in_arg_value, + const TestSampleType return_value) + { + SCORE_LANGUAGE_FUTURECPP_ASSERT(!in_arg_storage_.has_value()); + SCORE_LANGUAGE_FUTURECPP_ASSERT(!return_storage_.has_value()); + score::cpp::ignore = in_arg_storage_.emplace(in_arg_value); + score::cpp::ignore = return_storage_.emplace(return_value); + + score::cpp::span in_span{reinterpret_cast(&(in_arg_storage_.value())), + sizeof(TestSampleType)}; + score::cpp::span out_span{reinterpret_cast(&(return_storage_.value())), + sizeof(TestSampleType)}; + + return {in_span, out_span}; + } + + RuntimeMockGuard runtime_mock_guard_{}; + + SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard_{}; + SkeletonMethodBindingFactoryMockGuard skeleton_method_binding_factory_mock_guard_{}; + + mock_binding::SkeletonEvent skeleton_field_binding_mock_{}; + mock_binding::SkeletonMethod skeleton_field_get_binding_mock_{}; + mock_binding::SkeletonMethod skeleton_field_set_binding_mock_{}; + + std::optional in_arg_storage_{}; + std::optional return_storage_{}; +}; + TEST(SkeletonFieldTest, NotCopyable) { RecordProperty("Verifies", "SCR-18221574"); @@ -115,7 +175,8 @@ TEST(SkeletonFieldTest, SkeletonFieldContainsPublicSampleType) // When Ticket-104261 is implemented, the Update call does not have to be deferred until OfferService is called. This // test can be reworked to remove the call to PrepareOffer() and simply test Update() before PrepareOffer() is called. -TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOfferService) +using SkeletonFieldCopyUpdateTest = SkeletonFieldTestFixture; +TEST_F(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOfferService) { RecordProperty("Verifies", "SCR-17434775, SCR-17563743, SCR-21553554"); RecordProperty("Description", "Checks that calling Update before offer service defers the call to OfferService()."); @@ -123,26 +184,13 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOff RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; bool is_send_called_on_binding{false}; - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value and returns an empty result - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)) .WillOnce(InvokeWithoutArgs([&is_send_called_on_binding]() noexcept -> Result { is_send_called_on_binding = true; return {}; @@ -152,7 +200,7 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOff MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // then it does not return an error ASSERT_TRUE(update_result.has_value()); @@ -173,7 +221,7 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOff // When Ticket-104261 is implemented, the Update call does not have to be deferred until OfferService is called. This // test can be reworked to remove the call to PrepareOffer() and the deferred processing of Update() and simply test // Update() before PrepareOffer() is called. -TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServicePropagatesBindingFailureToOfferService) +TEST_F(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServicePropagatesBindingFailureToOfferService) { RecordProperty("Verifies", "SCR-17434775, SCR-21553554"); RecordProperty("Description", "Checks that calling Update before offer service defers the call to OfferService()."); @@ -181,26 +229,13 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServicePropagatesBindi RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; bool is_send_called_on_binding{false}; - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value and returns an error - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)) .WillOnce(InvokeWithoutArgs([&is_send_called_on_binding] { is_send_called_on_binding = true; return MakeUnexpected(ComErrc::kInvalidBindingInformation); @@ -210,7 +245,7 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServicePropagatesBindi MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // then it does not return an error ASSERT_TRUE(update_result.has_value()); @@ -229,7 +264,7 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServicePropagatesBindi EXPECT_TRUE(is_send_called_on_binding); } -TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServiceDispatchesToBinding) +TEST_F(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServiceDispatchesToBinding) { RecordProperty("Verifies", "SCR-17434775, SCR-21553375"); RecordProperty("Description", "Checks that calling Update after offer service dispatches to the binding."); @@ -237,35 +272,22 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServiceDispatchesToBind RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - const TestSampleType updated_value{43}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); + const TestSampleType updated_value{kDummyInitialValue + 1U}; // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value and returns an empty result - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)).WillOnce(Return(score::Result{})); // and Send will be called a second time on the event binding with the updated value and returns an empty result - EXPECT_CALL(skeleton_field_binding_mock, Send(updated_value, _)).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(updated_value, _)).WillOnce(Return(score::Result{})); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // then it does not return an error ASSERT_TRUE(update_result.has_value()); @@ -283,7 +305,7 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServiceDispatchesToBind ASSERT_TRUE(update_result_2.has_value()); } -TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServicePropagatesBindingFail) +TEST_F(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServicePropagatesBindingFail) { RecordProperty("Verifies", "SCR-17434775, SCR-21553375"); RecordProperty("Description", @@ -293,36 +315,23 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServicePropagatesBindin RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - const TestSampleType updated_value{43}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); + const TestSampleType updated_value{kDummyInitialValue + 1U}; // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value and returns an empty result - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)).WillOnce(Return(score::Result{})); // and Send will be called a second time on the event binding with the updated value and returns an error - EXPECT_CALL(skeleton_field_binding_mock, Send(updated_value, _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(updated_value, _)) .WillOnce(Return(MakeUnexpected(ComErrc::kInvalidBindingInformation))); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // then it does not return an error ASSERT_TRUE(update_result.has_value()); @@ -342,25 +351,15 @@ TEST(SkeletonFieldCopyUpdateTest, CallingUpdateAfterOfferServicePropagatesBindin } // This test can be removed when Ticket-104261 is implemented. -TEST(SkeletonFieldAllocateTest, CallingAllocateBeforePrepareOfferDoesNotReturnValidSlot) -{ - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); +using SkeletonFieldAllocateTest = SkeletonFieldTestFixture; - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).Times(0); +TEST_F(SkeletonFieldAllocateTest, CallingAllocateBeforePrepareOfferDoesNotReturnValidSlot) +{ + // and that PrepareOffer() will not be called on the event binding + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).Times(0); // and Allocate will not be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, Allocate()).Times(0); + EXPECT_CALL(skeleton_field_binding_mock_, Allocate()).Times(0); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -373,7 +372,7 @@ TEST(SkeletonFieldAllocateTest, CallingAllocateBeforePrepareOfferDoesNotReturnVa EXPECT_EQ(slot_result.error(), ComErrc::kBindingFailure); } -TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferDispatchesToBinding) +TEST_F(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferDispatchesToBinding) { RecordProperty("Verifies", "SCR-17434933, SCR-21470600"); RecordProperty("Description", "Checks that calling allocate after prepare offer dispatches to the binding."); @@ -381,35 +380,21 @@ TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferDispatchesToBind RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)); // and Allocate will be called again which returns a valid SampleAllocateePtr - EXPECT_CALL(skeleton_field_binding_mock, Allocate()) + EXPECT_CALL(skeleton_field_binding_mock_, Allocate()) .WillOnce(Return(ByMove(MakeSampleAllocateePtr(std::make_unique())))); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -427,7 +412,7 @@ TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferDispatchesToBind ASSERT_TRUE(slot_result.has_value()); } -TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferFailsWhenBindingReturnsError) +TEST_F(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferFailsWhenBindingReturnsError) { RecordProperty("Verifies", "SCR-17434933"); RecordProperty("Description", @@ -436,35 +421,21 @@ TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferFailsWhenBinding RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)); // and Allocate will be called again which returns a nullptr - EXPECT_CALL(skeleton_field_binding_mock, Allocate()) + EXPECT_CALL(skeleton_field_binding_mock_, Allocate()) .WillOnce(Return(ByMove(MakeUnexpected(ComErrc::kInvalidConfiguration)))); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -483,7 +454,9 @@ TEST(SkeletonFieldAllocateTest, CallingAllocateAfterPrepareOfferFailsWhenBinding EXPECT_EQ(slot_result.error(), ComErrc::kBindingFailure); } -TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceDispatchesToBinding) +using SkeletonFieldZeroCopyUpdateTest = SkeletonFieldTestFixture; + +TEST_F(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceDispatchesToBinding) { RecordProperty("Verifies", "SCR-17434778, SCR-21553623"); RecordProperty("Description", @@ -492,33 +465,20 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceDisp RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - const TestSampleType new_value{52}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); + const TestSampleType new_value{kDummyInitialValue + 1U}; // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)); // and Allocate will be called again which returns a valid SampleAllocateePtr - EXPECT_CALL(skeleton_field_binding_mock, Allocate()) + EXPECT_CALL(skeleton_field_binding_mock_, Allocate()) .WillOnce(Return(ByMove(MakeSampleAllocateePtr(std::make_unique())))); // and Send will be called a second time on the event binding with a new value which returns an empty result - EXPECT_CALL(skeleton_field_binding_mock, Send(An>(), _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(An>(), _)) .WillOnce(WithArg<0>(Invoke([new_value](SampleAllocateePtr sample_ptr) -> Result { EXPECT_EQ(*sample_ptr, new_value); return {}; @@ -528,7 +488,7 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceDisp MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -556,7 +516,7 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceDisp EXPECT_TRUE(new_update_result.has_value()); } -TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServicePropagatesBindingFail) +TEST_F(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServicePropagatesBindingFail) { RecordProperty("Verifies", "SCR-17434778"); RecordProperty( @@ -567,33 +527,20 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceProp RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - const TestSampleType new_value{52}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); + const TestSampleType new_value{kDummyInitialValue + 1U}; // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)); // and Allocate will be called again which returns a valid SampleAllocateePtr - EXPECT_CALL(skeleton_field_binding_mock, Allocate()) + EXPECT_CALL(skeleton_field_binding_mock_, Allocate()) .WillOnce(Return(ByMove(MakeSampleAllocateePtr(std::make_unique())))); // and Send will be called a second time on the event binding with a new value which returns an error - EXPECT_CALL(skeleton_field_binding_mock, Send(An>(), _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(An>(), _)) .WillOnce(WithArg<0>(Invoke([new_value](SampleAllocateePtr sample_ptr) -> Result { EXPECT_EQ(*sample_ptr, new_value); return MakeUnexpected(ComErrc::kInvalidBindingInformation); @@ -603,7 +550,7 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceProp MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -630,7 +577,9 @@ TEST(SkeletonFieldZeroCopyUpdateTest, CallingZeroCopyUpdateAfterOfferServiceProp EXPECT_EQ(update_result_2.error(), ComErrc::kBindingFailure); } -TEST(SkeletonFieldInitialValueFixture, LatestFieldValueWillBeSetOnPrepareOffer) +using SkeletonFieldInitialValueFixture = SkeletonFieldTestFixture; + +TEST_F(SkeletonFieldInitialValueFixture, LatestFieldValueWillBeSetOnPrepareOffer) { RecordProperty("Verifies", "SCR-22129134"); RecordProperty( @@ -640,32 +589,19 @@ TEST(SkeletonFieldInitialValueFixture, LatestFieldValueWillBeSetOnPrepareOffer) RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{42}; - const TestSampleType latest_value{43}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); + const TestSampleType latest_value{kDummyInitialValue + 1U}; // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called only once on the event binding with the latest value - EXPECT_CALL(skeleton_field_binding_mock, Send(latest_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(latest_value, _)); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -683,7 +619,7 @@ TEST(SkeletonFieldInitialValueFixture, LatestFieldValueWillBeSetOnPrepareOffer) EXPECT_TRUE(prepare_offer_result.has_value()); } -TEST(SkeletonFieldInitialValueFixture, OfferingFieldBeforeUpdatingValueReturnsError) +TEST_F(SkeletonFieldInitialValueFixture, OfferingFieldBeforeUpdatingValueReturnsError) { RecordProperty("Verifies", "SCR-17563743"); RecordProperty("Description", "Calling OfferService before setting the field value returns kFieldValueIsNotValid."); @@ -691,20 +627,8 @@ TEST(SkeletonFieldInitialValueFixture, OfferingFieldBeforeUpdatingValueReturnsEr RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will not be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).Times(0); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).Times(0); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -719,33 +643,19 @@ TEST(SkeletonFieldInitialValueFixture, OfferingFieldBeforeUpdatingValueReturnsEr EXPECT_EQ(result.error(), ComErrc::kFieldValueIsNotValid); } -TEST(SkeletonFieldInitialValueFixture, MoveConstructingFieldBeforePrepareOfferWillKeepInitialValue) +TEST_F(SkeletonFieldInitialValueFixture, MoveConstructingFieldBeforePrepareOfferWillKeepInitialValue) { - const TestSampleType initial_value{42}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - - // Expecting that a SkeletonField binding is created - auto skeleton_field_binding_mock_ptr = std::make_unique>(); - auto& skeleton_field_binding_mock = *skeleton_field_binding_mock_ptr; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // and that PrepareOffer() will be called on the event binding - EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -760,10 +670,9 @@ TEST(SkeletonFieldInitialValueFixture, MoveConstructingFieldBeforePrepareOfferWi EXPECT_TRUE(prepare_offer_result.has_value()); } -TEST(SkeletonFieldInitialValueFixture, MoveAssigningFieldBeforePrepareOfferWillKeepInitialValue) +TEST(SkeletonFieldInitialValueTest, MoveAssigningFieldBeforePrepareOfferWillKeepInitialValue) { - const TestSampleType initial_value{42}; - const TestSampleType initial_value_2{43}; + const TestSampleType kDummyInitialValue_2{kDummyInitialValue + 1U}; RuntimeMockGuard runtime_mock_guard{}; ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); @@ -783,13 +692,13 @@ TEST(SkeletonFieldInitialValueFixture, MoveAssigningFieldBeforePrepareOfferWillK EXPECT_CALL(skeleton_field_binding_mock, PrepareOffer()).WillOnce(Return(score::Result{})); // and Send will be called on the event binding with the initial value from the moved-from field - EXPECT_CALL(skeleton_field_binding_mock, Send(initial_value, _)); + EXPECT_CALL(skeleton_field_binding_mock, Send(kDummyInitialValue, _)); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When the initial value is set via an Update call - const auto update_result = unit.my_dummy_field_.Update(initial_value); + const auto update_result = unit.my_dummy_field_.Update(kDummyInitialValue); // which does not return an error EXPECT_TRUE(update_result.has_value()); @@ -813,7 +722,7 @@ TEST(SkeletonFieldInitialValueFixture, MoveAssigningFieldBeforePrepareOfferWillK MyDummySkeleton unit_2{std::make_unique(), identifier2}; // When the initial value is set via an Update call - const auto update_result_2 = unit_2.my_dummy_field_.Update(initial_value_2); + const auto update_result_2 = unit_2.my_dummy_field_.Update(kDummyInitialValue_2); // which does not return an error EXPECT_TRUE(update_result_2.has_value()); @@ -827,17 +736,8 @@ TEST(SkeletonFieldInitialValueFixture, MoveAssigningFieldBeforePrepareOfferWillK EXPECT_TRUE(prepare_offer_result.has_value()); } -TEST(SkeletonFieldTest, SkeletonFieldsRegisterThemselvesWithSkeleton) +TEST_F(SkeletonFieldTestFixture, SkeletonFieldsRegisterThemselvesWithSkeleton) { - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - // Expecting that the SkeletonFieldBindingFactory returns a valid binding - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -857,17 +757,8 @@ TEST(SkeletonFieldTest, SkeletonFieldsRegisterThemselvesWithSkeleton) EXPECT_EQ(&field, &unit.my_dummy_field_); } -TEST(SkeletonFieldTest, MovingConstructingSkeletonUpdatesFieldMapReference) +TEST_F(SkeletonFieldTestFixture, MovingConstructingSkeletonUpdatesFieldMapReference) { - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - // Expecting that the SkeletonFieldBindingFactory returns a valid binding - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -889,11 +780,8 @@ TEST(SkeletonFieldTest, MovingConstructingSkeletonUpdatesFieldMapReference) EXPECT_EQ(&field, &unit2.my_dummy_field_); } -TEST(SkeletonFieldTest, MovingAssigningSkeletonUpdatesFieldMapReference) +TEST_F(SkeletonFieldTestFixture, MovingAssigningSkeletonUpdatesFieldMapReference) { - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - ServiceIdentifierType service{make_ServiceIdentifierType("foo2", 1U, 0U)}; const ServiceInstanceDeployment instance_deployment{ service, @@ -903,12 +791,6 @@ TEST(SkeletonFieldTest, MovingAssigningSkeletonUpdatesFieldMapReference) InstanceIdentifier identifier2{make_InstanceIdentifier(instance_deployment, kTypeDeployment)}; // Expecting that the SkeletonFieldBindingFactory returns a valid binding for both Skeletons - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, CreateEventBinding(identifier2, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -933,17 +815,9 @@ TEST(SkeletonFieldTest, MovingAssigningSkeletonUpdatesFieldMapReference) EXPECT_EQ(&field, &unit2.my_dummy_field_); } -TEST(SkeletonFieldDeathTest, UpdateWithInvalidFieldNameTriggersTermination) +using SkeletonFieldDeathTest = SkeletonFieldTestFixture; +TEST_F(SkeletonFieldDeathTest, UpdateWithInvalidFieldNameTriggersTermination) { - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - // Expecting that the SkeletonFieldBindingFactory returns a valid binding - SkeletonFieldBindingFactoryMockGuard skeleton_field_binding_factory_mock_guard{}; - EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - // Given a skeleton created based on a Lola binding MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -960,7 +834,6 @@ TEST(SkeletonFieldDeathTest, UpdateWithInvalidFieldNameTriggersTermination) } // Helper skeleton that holds an EnableSet=true field (setter-capable field) - class MySetterSkeleton : public SkeletonBase { public: @@ -969,9 +842,8 @@ class MySetterSkeleton : public SkeletonBase SkeletonField my_setter_field_{*this, kFieldName}; }; -// Static type-trait tests for RegisterSetHandler availability - -TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerOnlyExistsWhenEnableSetIsTrue) +/// gtodo: What is this test testing?? +TEST(SkeletonFieldSetHandlerTypeTraitsTest, RegisterSetHandlerOnlyExistsWhenEnableSetIsTrue) { RecordProperty("Description", "RegisterSetHandler() shall only exist on SkeletonField. " @@ -990,38 +862,14 @@ TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerOnlyExistsWhenEnableSetIsTru "EnableSet=false and EnableSet=true fields must be different types"); } -// RegisterSetHandler – happy-path: handler forwarded to method binding +using SkeletonFieldSetHandlerTest = SkeletonFieldTestFixture; -TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerForwardsToMethodBinding) +TEST_F(SkeletonFieldSetHandlerTest, RegisterSetHandlerForwardsToMethodBinding) { - RecordProperty("Description", - "Calling RegisterSetHandler() on an EnableSet=true SkeletonField shall forward " - "the handler registration to the underlying SkeletonMethod binding and return " - "success."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - - // Inject a mock SkeletonMethodBinding so that RegisterHandler is interceptable. - mock_binding::SkeletonMethod method_binding_mock{}; - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) - .WillOnce(Return(ByMove(std::make_unique(method_binding_mock)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); - - // The method binding's RegisterHandler must be called exactly once and returns success. - EXPECT_CALL(method_binding_mock, RegisterHandler(_)).WillOnce(Return(Result{})); + // Expecting that RegisterHandler is called on the field set method binding which returns success + EXPECT_CALL(skeleton_field_set_binding_mock_, RegisterHandler(_)).WillOnce(Return(Result{})); - // Given a setter-capable skeleton + // Given a skeleton containing a field with a setter enabled MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When RegisterSetHandler is called with a valid (no-op) handler @@ -1031,36 +879,13 @@ TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerForwardsToMethodBinding) EXPECT_TRUE(result.has_value()); } -// RegisterSetHandler – failure propagation from the method binding - -TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerPropagatesBindingError) +TEST_F(SkeletonFieldSetHandlerTest, RegisterSetHandlerPropagatesBindingError) { - RecordProperty("Description", - "When the underlying SkeletonMethod binding returns an error from RegisterHandler, " - "RegisterSetHandler() shall propagate that error unchanged."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - - mock_binding::SkeletonMethod method_binding_mock{}; - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) - .WillOnce(Return(ByMove(std::make_unique(method_binding_mock)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); - - // The method binding returns an error - EXPECT_CALL(method_binding_mock, RegisterHandler(_)) + // Expecting that RegisterHandler is called on the field set method binding which returns an error + EXPECT_CALL(skeleton_field_set_binding_mock_, RegisterHandler(_)) .WillOnce(Return(MakeUnexpected(ComErrc::kCommunicationLinkError))); + // Given a skeleton containing a field with a setter enabled MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // When RegisterSetHandler is called @@ -1071,34 +896,12 @@ TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerPropagatesBindingError) EXPECT_EQ(result.error(), ComErrc::kCommunicationLinkError); } -// PrepareOffer fails when EnableSet=true but no handler has been registered - -TEST(SkeletonFieldSetHandlerTest, PrepareOfferFailsWhenSetHandlerNotRegistered) +TEST_F(SkeletonFieldSetHandlerTest, PrepareOfferFailsWhenSetHandlerNotRegistered) { - RecordProperty("Verifies", "SCR-17563743"); - RecordProperty("Description", - "When a SkeletonField is defined with EnableSet=true and no set handler has been " - "registered, PrepareOffer() shall return kSetHandlerNotSet."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::make_unique>()))); - - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, _)) - .WillOnce(Return(ByMove(nullptr))) - .WillOnce(Return(ByMove(nullptr))); - + // Given a skeleton containing a field with a setter enabled MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; - // Set an initial value so that the initial-value check does not fire first + // and given an initial value was set so that the initial-value check does not fail EXPECT_TRUE(unit.my_setter_field_.Update(TestSampleType{42}).has_value()); // When PrepareOffer is called without having called RegisterSetHandler @@ -1109,50 +912,18 @@ TEST(SkeletonFieldSetHandlerTest, PrepareOfferFailsWhenSetHandlerNotRegistered) EXPECT_EQ(result.error(), ComErrc::kSetHandlerNotSet); } -// PrepareOffer succeeds when EnableSet=true and handler IS registered - -TEST(SkeletonFieldSetHandlerTest, PrepareOfferSucceedsAfterRegisterSetHandler) +TEST_F(SkeletonFieldSetHandlerTest, PrepareOfferSucceedsAfterRegisterSetHandler) { - RecordProperty("Description", - "When an EnableSet=true SkeletonField has a set handler registered and an initial " - "value set, PrepareOffer() shall succeed."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - const TestSampleType initial_value{7U}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - mock_binding::SkeletonMethod method_binding_mock{}; - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) - .WillOnce(Return(ByMove(std::make_unique(method_binding_mock)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); - - // The binding's RegisterHandler returns success - EXPECT_CALL(method_binding_mock, RegisterHandler(_)).WillOnce(Return(Result{})); - - // PrepareOffer on the event binding and the initial-value Send must succeed - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(initial_value, _)).WillOnce(Return(Result{})); + const TestSampleType kDummyInitialValue{7U}; + // Given a skeleton containing a field with a setter enabled MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // Register a valid (no-op) set handler ASSERT_TRUE(unit.my_setter_field_.RegisterSetHandler([](TestSampleType& /*value*/) noexcept {}).has_value()); // Set the initial field value - ASSERT_TRUE(unit.my_setter_field_.Update(initial_value).has_value()); + ASSERT_TRUE(unit.my_setter_field_.Update(kDummyInitialValue).has_value()); // When PrepareOffer is called const auto result = unit.my_setter_field_.PrepareOffer(); @@ -1161,45 +932,22 @@ TEST(SkeletonFieldSetHandlerTest, PrepareOfferSucceedsAfterRegisterSetHandler) EXPECT_TRUE(result.has_value()); } -// EnableSet=false: PrepareOffer does NOT require a registered set handler - -TEST(SkeletonFieldSetHandlerTest, PrepareOfferSucceedsWithoutHandlerWhenEnableSetIsFalse) +TEST_F(SkeletonFieldSetHandlerTest, PrepareOfferSucceedsWithoutHandlerWhenEnableSetIsFalse) { - RecordProperty("Description", - "When a SkeletonField has EnableSet=false (no setter), PrepareOffer() shall " - "succeed without registering a set handler."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - const TestSampleType initial_value{5U}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(initial_value, _)).WillOnce(Return(Result{})); - - // A non-setter skeleton (EnableSet=false) + // Given a skeleton containing a field without a setter enabled MyDummySkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; - ASSERT_TRUE(unit.my_dummy_field_.Update(initial_value).has_value()); + ASSERT_TRUE(unit.my_dummy_field_.Update(kDummyInitialValue).has_value()); - // PrepareOffer must succeed even without a RegisterSetHandler call + // When PrepareOffer is called without registering a set handler const auto result = unit.my_dummy_field_.PrepareOffer(); + + // Then it succeeds EXPECT_TRUE(result.has_value()); } -// RegisterSetHandler accepts any callable with the expected signature - -TEST(SkeletonFieldSetHandlerTest, RegisterSetHandlerAcceptsAnyCallable) +// gtodo: What is this testing??? +TEST(SkeletonFieldSetHandlerTypeTraitsTest, RegisterSetHandlerAcceptsAnyCallable) { RecordProperty("Description", "RegisterSetHandler() shall accept any callable (lambda, std::function, " @@ -1236,7 +984,7 @@ class CapturingSkeletonMethodBinding : public SkeletonMethodBinding TypeErasedHandler captured_handler_{}; }; -TEST(SkeletonFieldSetHandlerTest, UserCallbackIsInvokedByWrappedHandler) +TEST_F(SkeletonFieldSetHandlerTest, UserCallbackIsInvokedByWrappedHandler) { RecordProperty("Description", "The callback registered with RegisterSetHandler() is invoked by the wrapped " @@ -1248,31 +996,17 @@ TEST(SkeletonFieldSetHandlerTest, UserCallbackIsInvokedByWrappedHandler) const TestSampleType incoming_value{99U}; bool user_callback_called{false}; - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - // PrepareOffer and the deferred Send for the initial value - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); - // The wrapped handler also calls Update(incoming_value) → Send on the event binding - EXPECT_CALL(event_binding, Send(incoming_value, _)).WillOnce(Return(Result{})); - // Use a capturing binding so that we can invoke the type-erased handler later auto capturing_binding = std::make_unique(); auto& capturing_binding_ref = *capturing_binding; - - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) .WillOnce(Return(ByMove(std::move(capturing_binding)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) + .WillOnce(InvokeWithoutArgs([]() { + return std::unique_ptr{}; + })); MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -1290,13 +1024,7 @@ TEST(SkeletonFieldSetHandlerTest, UserCallbackIsInvokedByWrappedHandler) // Simulate the proxy invoking the setter by calling the captured type-erased handler. // The SkeletonMethod serializes the incoming value into a byte span before dispatch. // We replicate that serialization here for the single TestSampleType argument. - using InArgStorage = TestSampleType; - InArgStorage in_arg{incoming_value}; - TestSampleType return_storage{}; - std::optional> in_span{ - score::cpp::span{reinterpret_cast(&in_arg), sizeof(InArgStorage)}}; - std::optional> out_span{ - score::cpp::span{reinterpret_cast(&return_storage), sizeof(TestSampleType)}}; + auto [in_span, out_span] = CreateFieldSetterInArgAndReturnSpans(incoming_value, TestSampleType{}); capturing_binding_ref.captured_handler_(in_span, out_span); @@ -1304,8 +1032,7 @@ TEST(SkeletonFieldSetHandlerTest, UserCallbackIsInvokedByWrappedHandler) EXPECT_TRUE(user_callback_called); } -// Handler wrapping: user callback may modify the value in-place -TEST(SkeletonFieldSetHandlerTest, UserCallbackCanModifyValueInPlace) +TEST_F(SkeletonFieldSetHandlerTest, UserCallbackCanModifyValueInPlace) { RecordProperty("Description", "The set handler callback receives the value by reference. Modifications made " @@ -1318,29 +1045,21 @@ TEST(SkeletonFieldSetHandlerTest, UserCallbackCanModifyValueInPlace) const TestSampleType incoming_value{10U}; const TestSampleType modified_value{20U}; // callback doubles the value - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); // The modified value (20) must be the one forwarded to the event binding - EXPECT_CALL(event_binding, Send(modified_value, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(modified_value, _)).WillOnce(Return(Result{})); auto capturing_binding = std::make_unique(); auto& capturing_binding_ref = *capturing_binding; - - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) .WillOnce(Return(ByMove(std::move(capturing_binding)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) + .WillOnce(InvokeWithoutArgs([]() { + return std::unique_ptr{}; + })); MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -1355,19 +1074,13 @@ TEST(SkeletonFieldSetHandlerTest, UserCallbackCanModifyValueInPlace) ASSERT_TRUE(unit.my_setter_field_.PrepareOffer().has_value()); // Invoke the wrapped handler with incoming_value (10) - TestSampleType in_arg{incoming_value}; - TestSampleType return_storage{}; - std::optional> in_span{ - score::cpp::span{reinterpret_cast(&in_arg), sizeof(in_arg)}}; - std::optional> out_span{ - score::cpp::span{reinterpret_cast(&return_storage), sizeof(TestSampleType)}}; + auto [in_span, out_span] = CreateFieldSetterInArgAndReturnSpans(incoming_value, TestSampleType{}); // The handler shall call Send with 20, not 10 capturing_binding_ref.captured_handler_(in_span, out_span); } -// Handler wrapping: Update() failure inside the wrapped handler is logged, not propagated -TEST(SkeletonFieldSetHandlerTest, WrappedHandlerLogsWhenUpdateFails) +TEST_F(SkeletonFieldSetHandlerTest, WrappedHandlerLogsWhenUpdateFails) { RecordProperty("Description", "When the event binding's Send() fails inside the wrapped set handler, the " @@ -1381,30 +1094,22 @@ TEST(SkeletonFieldSetHandlerTest, WrappedHandlerLogsWhenUpdateFails) const TestSampleType incoming_value{55U}; bool user_callback_called{false}; - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); // Simulate Update() failure when the wrapped handler is invoked by the proxy - EXPECT_CALL(event_binding, Send(incoming_value, _)) + EXPECT_CALL(skeleton_field_binding_mock_, Send(incoming_value, _)) .WillOnce(Return(MakeUnexpected(ComErrc::kCommunicationLinkError))); auto capturing_binding = std::make_unique(); auto& capturing_binding_ref = *capturing_binding; - - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) .WillOnce(Return(ByMove(std::move(capturing_binding)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) + .WillOnce(InvokeWithoutArgs([]() { + return std::unique_ptr{}; + })); MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -1417,12 +1122,7 @@ TEST(SkeletonFieldSetHandlerTest, WrappedHandlerLogsWhenUpdateFails) ASSERT_TRUE(unit.my_setter_field_.Update(TestSampleType{1U}).has_value()); ASSERT_TRUE(unit.my_setter_field_.PrepareOffer().has_value()); - TestSampleType in_arg{incoming_value}; - TestSampleType return_storage{}; - std::optional> in_span{ - score::cpp::span{reinterpret_cast(&in_arg), sizeof(in_arg)}}; - std::optional> out_span{ - score::cpp::span{reinterpret_cast(&return_storage), sizeof(TestSampleType)}}; + auto [in_span, out_span] = CreateFieldSetterInArgAndReturnSpans(incoming_value, TestSampleType{}); // Handler must complete normally even when Update() returns an error capturing_binding_ref.captured_handler_(in_span, out_span); @@ -1431,8 +1131,7 @@ TEST(SkeletonFieldSetHandlerTest, WrappedHandlerLogsWhenUpdateFails) EXPECT_TRUE(user_callback_called); } -// RegisterSetHandler sets is_set_handler_registered_ flag -TEST(SkeletonFieldSetHandlerTest, IsSetHandlerRegisteredFlagIsSetAfterRegistration) +TEST_F(SkeletonFieldSetHandlerTest, IsSetHandlerRegisteredFlagIsSetAfterRegistration) { RecordProperty("Description", "After a successful RegisterSetHandler() call the internal flag " @@ -1441,33 +1140,17 @@ TEST(SkeletonFieldSetHandlerTest, IsSetHandlerRegisteredFlagIsSetAfterRegistrati RecordProperty("Priority", "1"); RecordProperty("DerivationTechnique", "Analysis of requirements"); - const TestSampleType initial_value{3U}; - - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); + const TestSampleType kDummyInitialValue{3U}; - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(initial_value, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(kDummyInitialValue, _)).WillOnce(Return(Result{})); - mock_binding::SkeletonMethod method_binding_mock{}; - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) - .WillOnce(Return(ByMove(std::make_unique(method_binding_mock)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) - .WillOnce(Return(ByMove(nullptr))); - EXPECT_CALL(method_binding_mock, RegisterHandler(_)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_set_binding_mock_, RegisterHandler(_)).WillOnce(Return(Result{})); MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; // Before registration PrepareOffer should fail with kSetHandlerNotSet - ASSERT_TRUE(unit.my_setter_field_.Update(initial_value).has_value()); + ASSERT_TRUE(unit.my_setter_field_.Update(kDummyInitialValue).has_value()); { // Separate scope: verify failure without handler // (We cannot call PrepareOffer twice without a stop-offer in between, so we @@ -1482,8 +1165,7 @@ TEST(SkeletonFieldSetHandlerTest, IsSetHandlerRegisteredFlagIsSetAfterRegistrati EXPECT_TRUE(result.has_value()); } -// RegisterSetHandler called twice: second call replaces the handler -TEST(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler) +TEST_F(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler) { RecordProperty("Description", "Calling RegisterSetHandler() a second time shall replace the previously stored " @@ -1497,28 +1179,18 @@ TEST(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler) bool first_callback_called{false}; bool second_callback_called{false}; - RuntimeMockGuard runtime_mock_guard{}; - ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); - - SkeletonFieldBindingFactoryMockGuard field_binding_factory_guard{}; - auto event_binding_ptr = std::make_unique>(); - auto& event_binding = *event_binding_ptr; - EXPECT_CALL(field_binding_factory_guard.factory_mock_, - CreateEventBinding(kInstanceIdWithLolaBinding, _, kFieldName)) - .WillOnce(Return(ByMove(std::move(event_binding_ptr)))); - - EXPECT_CALL(event_binding, PrepareOffer()).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); - EXPECT_CALL(event_binding, Send(incoming_value, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, PrepareOffer()).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(TestSampleType{1U}, _)).WillOnce(Return(Result{})); + EXPECT_CALL(skeleton_field_binding_mock_, Send(incoming_value, _)).WillOnce(Return(Result{})); // The method binding is called twice (once per RegisterSetHandler) auto capturing_binding = std::make_unique(); auto& capturing_binding_ref = *capturing_binding; - - SkeletonMethodBindingFactoryMockGuard method_binding_factory_guard{}; - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kSet)) .WillOnce(Return(ByMove(std::move(capturing_binding)))); - EXPECT_CALL(method_binding_factory_guard.factory_mock_, Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(kInstanceIdWithLolaBinding, _, _, MethodType::kGet)) .WillOnce(Return(ByMove(nullptr))); MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; @@ -1541,12 +1213,7 @@ TEST(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler) ASSERT_TRUE(unit.my_setter_field_.PrepareOffer().has_value()); // Invoke the most-recently captured handler (from the second registration) - TestSampleType in_arg{incoming_value}; - TestSampleType return_storage{}; - std::optional> in_span{ - score::cpp::span{reinterpret_cast(&in_arg), sizeof(in_arg)}}; - std::optional> out_span{ - score::cpp::span{reinterpret_cast(&return_storage), sizeof(TestSampleType)}}; + auto [in_span, out_span] = CreateFieldSetterInArgAndReturnSpans(incoming_value, TestSampleType{}); capturing_binding_ref.captured_handler_(in_span, out_span); @@ -1555,5 +1222,26 @@ TEST(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler) EXPECT_TRUE(second_callback_called); } +using SkeletonFieldMoveConstructionFixture = SkeletonFieldTestFixture; +TEST_F(SkeletonFieldMoveConstructionFixture, SecondRegisterSetHandlerReplacesHandler) +{ + // Note. This test verifies that moving a skeleton does not break the getter / setter methods stored within a field. + // When moving, UpdateSkeletonReference must update the references to SkeletonBase in the field instance as well as + // the stored methods. However, the SkeletonBase reference in the methods are only used when moving the method (to + // update the reference to the method in SkeletonBase). Since the methods are stored in the field as unique_ptrs, + // they will never actually be moved. Therefore, with the current implementation, the SkeletonBase reference in the + // getter and setter are never used and so we have no way of ensuring that they are updated correctly. We can only + // verify that the method is still valid after move construction. + + // Given a skeleton containing a field with a setter enabled + MySetterSkeleton unit{std::make_unique(), kInstanceIdWithLolaBinding}; + + // When move constructing the skeleton + MySetterSkeleton unit2{std::move(unit)}; + + // Then the method should still be usable (validated by calling RegisterSetHandler which dispatches to the method) + unit2.my_setter_field_.RegisterSetHandler([](TestSampleType& /*value*/) noexcept {}); +} + } // namespace } // namespace score::mw::com::impl diff --git a/score/mw/com/impl/tracing/skeleton_tracing_test.cpp b/score/mw/com/impl/tracing/skeleton_tracing_test.cpp index f2d395cdb..e987fc35f 100644 --- a/score/mw/com/impl/tracing/skeleton_tracing_test.cpp +++ b/score/mw/com/impl/tracing/skeleton_tracing_test.cpp @@ -67,6 +67,8 @@ class MyDummyField : public SkeletonFieldBase { } + void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override {} + bool IsInitialValueSaved() const noexcept override { return true; @@ -77,9 +79,9 @@ class MyDummyField : public SkeletonFieldBase return {}; } - bool IsSetHandlerRegistered() const noexcept override + bool IsSetHandlerMissing() const noexcept override { - return true; + return false; } }; diff --git a/score/mw/com/impl/traits_test.cpp b/score/mw/com/impl/traits_test.cpp index 7074a6829..f99961ee5 100644 --- a/score/mw/com/impl/traits_test.cpp +++ b/score/mw/com/impl/traits_test.cpp @@ -12,6 +12,7 @@ ********************************************************************************/ #include "score/mw/com/impl/traits.h" +#include "method_type.h" #include "score/mw/com/impl/bindings/mock_binding/proxy_method.h" #include "score/mw/com/impl/bindings/mock_binding/skeleton.h" #include "score/mw/com/impl/bindings/mock_binding/skeleton_method.h" @@ -67,7 +68,7 @@ class MyInterface : public InterfaceTrait::Base using InterfaceTrait::Base::Base; typename InterfaceTrait::template Event some_event{*this, kEventName}; - typename InterfaceTrait::template Field some_field{*this, kFieldName}; + typename InterfaceTrait::template Field some_field{*this, kFieldName}; typename InterfaceTrait::template Method some_method{*this, kMethodName}; }; using MyProxy = AsProxy; @@ -96,7 +97,6 @@ class ProxyCreationFixture : public ::testing::Test public: void SetUp() override { - auto proxy_binding_mock_ptr = std::make_unique(proxy_binding_mock_); auto proxy_event_binding_mock_ptr = std::make_unique>(proxy_event_binding_mock_); @@ -104,6 +104,10 @@ class ProxyCreationFixture : public ::testing::Test std::make_unique>(proxy_field_binding_mock_); auto proxy_method_binding_mock_ptr = std::make_unique(proxy_method_binding_mock_); + auto proxy_field_get_binding_mock_ptr = + std::make_unique(proxy_field_get_binding_mock_); + auto proxy_field_set_binding_mock_ptr = + std::make_unique(proxy_field_set_binding_mock_); auto& runtime_mock = runtime_mock_guard_.runtime_mock_; // By default the runtime configuration has no GetTracingFilterConfig @@ -122,8 +126,12 @@ class ProxyCreationFixture : public ::testing::Test .WillByDefault(Return(ByMove(std::move(proxy_field_binding_mock_ptr)))); // By default the Create call on the ProxyMethodBindingFactory returns valid bindings. - ON_CALL(proxy_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, kMethodName, _)) + ON_CALL(proxy_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, kMethodName, MethodType::kMethod)) .WillByDefault(Return(ByMove(std::move(proxy_method_binding_mock_ptr)))); + ON_CALL(proxy_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, kFieldName, MethodType::kSet)) + .WillByDefault(Return(ByMove(std::move(proxy_field_set_binding_mock_ptr)))); + ON_CALL(proxy_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, kFieldName, MethodType::kGet)) + .WillByDefault(Return(ByMove(std::move(proxy_field_get_binding_mock_ptr)))); // By default that the proxy_binding can successfully call SetupMethods ON_CALL(proxy_binding_mock_, SetupMethods()).WillByDefault(Return(score::Result{})); @@ -146,6 +154,8 @@ class ProxyCreationFixture : public ::testing::Test mock_binding::ProxyEvent proxy_event_binding_mock_{}; mock_binding::ProxyEvent proxy_field_binding_mock_{}; mock_binding::ProxyMethod proxy_method_binding_mock_{}; + mock_binding::ProxyMethod proxy_field_set_binding_mock_{}; + mock_binding::ProxyMethod proxy_field_get_binding_mock_{}; }; TEST(GeneratedProxyTest, NotCopyable) @@ -461,6 +471,10 @@ class SkeletonCreationFixture : public ::testing::Test std::make_unique>(skeleton_field_binding_mock_); auto skeleton_method_binding_mock_ptr = std::make_unique(skeleton_method_binding_mock_); + auto skeleton_field_get_binding_mock_ptr = + std::make_unique(skeleton_field_get_binding_mock_); + auto skeleton_field_set_binding_mock_ptr = + std::make_unique(skeleton_field_set_binding_mock_); auto& runtime_mock = runtime_mock_guard_.runtime_mock_; // By default the runtime configuration has no GetTracingFilterConfig @@ -481,18 +495,15 @@ class SkeletonCreationFixture : public ::testing::Test .WillByDefault(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); // By default the Create call on the SkeletonMethodBindingFactory returns valid bindings. - // Broad catch-all for field get/set method bindings (defined before the specific kMethodName matcher - // so that the more specific ON_CALL takes precedence for kMethodName calls). - ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)) - .WillByDefault(Invoke([this](const InstanceIdentifier&, - SkeletonBinding*, - const std::string_view, - MethodType) -> std::unique_ptr { - return std::make_unique(skeleton_field_method_binding_mock_); - })); ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, - Create(identifier_with_valid_binding_, _, kMethodName, _)) + Create(identifier_with_valid_binding_, _, kMethodName, MethodType::kMethod)) .WillByDefault(Return(ByMove(std::move(skeleton_method_binding_mock_ptr)))); + ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kSet)) + .WillByDefault(Return(ByMove(std::move(skeleton_field_set_binding_mock_ptr)))); + ON_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kGet)) + .WillByDefault(Return(ByMove(std::move(skeleton_field_get_binding_mock_ptr)))); // By default the runtime configuration resolves instance identifiers resolved_instance_identifiers_.push_back(identifier_with_valid_binding_); @@ -519,7 +530,8 @@ class SkeletonCreationFixture : public ::testing::Test mock_binding::SkeletonEvent skeleton_event_binding_mock_{}; mock_binding::SkeletonEvent skeleton_field_binding_mock_{}; mock_binding::SkeletonMethod skeleton_method_binding_mock_{}; - mock_binding::SkeletonMethod skeleton_field_method_binding_mock_{}; + mock_binding::SkeletonMethod skeleton_field_set_binding_mock_{}; + mock_binding::SkeletonMethod skeleton_field_get_binding_mock_{}; }; using GeneratedSkeletonCreationInstanceSpecifierTestFixture = SkeletonCreationFixture; @@ -539,6 +551,10 @@ TEST_F(GeneratedSkeletonCreationInstanceSpecifierTestFixture, std::make_unique>(skeleton_field_binding_mock_); auto skeleton_method_binding_mock_ptr = std::make_unique(skeleton_method_binding_mock_); + auto skeleton_field_set_binding_mock_ptr = + std::make_unique(skeleton_field_set_binding_mock_); + auto skeleton_field_get_binding_mock_ptr = + std::make_unique(skeleton_field_get_binding_mock_); // Expecting that valid bindings are created for the Skeleton, SkeletonEvent and SkeletonField EXPECT_CALL(skeleton_binding_factory_mock_guard_.factory_mock_, Create(identifier_with_valid_binding_)) @@ -549,15 +565,14 @@ TEST_F(GeneratedSkeletonCreationInstanceSpecifierTestFixture, EXPECT_CALL(skeleton_field_binding_factory_mock_guard_.factory_mock_, CreateEventBinding(identifier_with_valid_binding_, _, kFieldName)) .WillOnce(Return(ByMove(std::move(skeleton_field_binding_mock_ptr)))); - // Field get method binding (not yet fully implemented; set method only exists when EnableSet=true) - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)) - .Times(1) - .WillOnce(Invoke([this](const InstanceIdentifier&, SkeletonBinding*, const std::string_view, MethodType) - -> std::unique_ptr { - return std::make_unique(skeleton_field_method_binding_mock_); - })); EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, - Create(identifier_with_valid_binding_, _, kMethodName, _)) + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kSet)) + .WillOnce(Return(ByMove(std::move(skeleton_field_set_binding_mock_ptr)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kGet)) + .WillOnce(Return(ByMove(std::move(skeleton_field_get_binding_mock_ptr)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kMethodName, MethodType::kMethod)) .WillOnce(Return(ByMove(std::move(skeleton_method_binding_mock_ptr)))); // When constructing a skeleton with an InstanceSpecifier @@ -644,11 +659,13 @@ TEST_F(GeneratedSkeletonCreationInstanceSpecifierTestFixture, RecordProperty("DerivationTechnique", "Analysis of requirements"); // Expecting that the Create call on the SkeletonMethodBindingFactory returns an invalid binding for the method. - // Field get method binding (not yet fully implemented; set method only exists when EnableSet=true) - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)).Times(1); EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, - Create(identifier_with_valid_binding_, _, kMethodName, _)) + Create(_, _, kMethodName, MethodType::kMethod)) .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kSet)); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kGet)); // When constructing a skeleton with an InstanceSpecifier const auto unit = MySkeleton::Create(kInstanceSpecifier); @@ -777,13 +794,13 @@ TEST_F(GeneratedSkeletonCreationInstanceIdentifierTestFixture, ConstructingFromI TEST_F(GeneratedSkeletonCreationInstanceIdentifierTestFixture, ConstructingFromInvalidSkeletonMethodReturnsError) { - // Expecting that the Create call on the SkeletonMethodBindingFactory returns an invalid binding for the method. - // Field get method binding (not yet fully implemented; set method only exists when EnableSet=true) - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)).Times(1); - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, - Create(identifier_with_valid_binding_, _, kMethodName, _)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kMethod)) .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kSet)); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, + Create(identifier_with_valid_binding_, _, kFieldName, MethodType::kGet)); // When constructing a skeleton with an InstanceIdentifier const auto unit = MySkeleton::Create(identifier_with_valid_binding_); @@ -843,6 +860,9 @@ TEST_F(GeneratedSkeletonCreationInstanceIdentifierTestFixture, CanInterpretAsSke // and updating the field value std::ignore = unit.some_field.Update(field_value); + // and registering a field set handler + unit.some_field.RegisterSetHandler([](TestSampleType&) {}); + // and offering the service const auto result = unit.OfferService(); EXPECT_TRUE(result.has_value()); @@ -907,6 +927,9 @@ class GeneratedSkeletonStopOfferServiceRaiiFixture : public SkeletonCreationFixt const auto update_result = skeleton.some_field.Update(field_value); ASSERT_TRUE(update_result.has_value()); + const auto register_result = skeleton.some_field.RegisterSetHandler([](TestSampleType&) {}); + ASSERT_TRUE(register_result.has_value()); + const auto offer_result = skeleton.OfferService(); ASSERT_TRUE(offer_result.has_value()); } @@ -932,15 +955,15 @@ class GeneratedSkeletonStopOfferServiceRaiiFixture : public SkeletonCreationFixt EXPECT_CALL(skeleton_field_binding_factory_mock_guard_.factory_mock_, CreateEventBinding(_, _, _)) .WillOnce(Return(ByMove( std::make_unique>(skeleton_field_binding_mock_)))); - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kSet)) .WillOnce( - Return(ByMove(std::make_unique(skeleton_method_binding_mock_)))) - .WillRepeatedly(Invoke([this](const InstanceIdentifier&, - SkeletonBinding*, - const std::string_view, - MethodType) -> std::unique_ptr { - return std::make_unique(skeleton_field_method_binding_mock_); - })); + Return(ByMove(std::make_unique(skeleton_field_set_binding_mock_)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kGet)) + .WillOnce( + Return(ByMove(std::make_unique(skeleton_field_get_binding_mock_)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kMethod)) + .WillOnce( + Return(ByMove(std::make_unique(skeleton_method_binding_mock_)))); EXPECT_CALL(skeleton_binding_factory_mock_guard_.factory_mock_, Create(_)) .WillOnce(Return(ByMove(std::make_unique(skeleton_binding_mock_2_)))); @@ -950,15 +973,15 @@ class GeneratedSkeletonStopOfferServiceRaiiFixture : public SkeletonCreationFixt EXPECT_CALL(skeleton_field_binding_factory_mock_guard_.factory_mock_, CreateEventBinding(_, _, _)) .WillOnce(Return(ByMove( std::make_unique>(skeleton_field_binding_mock_2_)))); - EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, _)) + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kSet)) + .WillOnce(Return( + ByMove(std::make_unique(skeleton_field_set_binding_mock_2_)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kGet)) + .WillOnce(Return( + ByMove(std::make_unique(skeleton_field_get_binding_mock_2_)))); + EXPECT_CALL(skeleton_method_binding_factory_mock_guard_.factory_mock_, Create(_, _, _, MethodType::kMethod)) .WillOnce( - Return(ByMove(std::make_unique(skeleton_method_binding_mock_2_)))) - .WillRepeatedly(Invoke([this](const InstanceIdentifier&, - SkeletonBinding*, - const std::string_view, - MethodType) -> std::unique_ptr { - return std::make_unique(skeleton_field_method_binding_mock_); - })); + Return(ByMove(std::make_unique(skeleton_method_binding_mock_2_)))); score::cpp::ignore = skeleton_.emplace(CreateService()); score::cpp::ignore = skeleton_2_.emplace(CreateService()); @@ -997,6 +1020,8 @@ class GeneratedSkeletonStopOfferServiceRaiiFixture : public SkeletonCreationFixt mock_binding::SkeletonEvent skeleton_event_binding_mock_2_{}; mock_binding::SkeletonEvent skeleton_field_binding_mock_2_{}; mock_binding::SkeletonMethod skeleton_method_binding_mock_2_{}; + mock_binding::SkeletonMethod skeleton_field_set_binding_mock_2_{}; + mock_binding::SkeletonMethod skeleton_field_get_binding_mock_2_{}; std::optional skeleton_{}; std::optional skeleton_2_{}; diff --git a/score/mw/com/test/methods/methods_test_resources/method_provider.h b/score/mw/com/test/methods/methods_test_resources/method_provider.h index ff36eed65..134779503 100644 --- a/score/mw/com/test/methods/methods_test_resources/method_provider.h +++ b/score/mw/com/test/methods/methods_test_resources/method_provider.h @@ -109,10 +109,11 @@ bool MethodProvider::RegisterMethodHandlerWithInArgsAndReturn() { SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(skeleton_ != nullptr); - auto handler_with_in_args_and_return = [](std::int32_t a, std::int32_t b) -> std::int32_t { - std::cout << "Provider: with_in_args_and_return called with " << a << " + " << b << std::endl; - return a + b; - }; + auto handler_with_in_args_and_return = + [](std::int32_t& return_value, const std::int32_t& a, const std::int32_t& b) { + std::cout << "Provider: with_in_args_and_return called with " << a << " + " << b << std::endl; + return_value = a + b; + }; const auto register_result = skeleton_->with_in_args_and_return.RegisterHandler(std::move(handler_with_in_args_and_return)); if (!register_result) @@ -129,8 +130,8 @@ template bool MethodProvider::RegisterMethodHandlerWithInArgsOnly(const std::int32_t expected_input_argument_a, const std::int32_t expected_input_argument_b) { - auto handler_with_in_args_only = [expected_input_argument_a, expected_input_argument_b](std::int32_t a, - std::int32_t b) { + auto handler_with_in_args_only = [expected_input_argument_a, expected_input_argument_b](const std::int32_t& a, + const std::int32_t& b) { std::cout << "Provider: with_in_args_only called with " << a << " + " << b << std::endl; SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(a == expected_input_argument_a, "Unexpected first InArg received!"); SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(b == expected_input_argument_b, "Unexpected second InArg received!"); @@ -149,9 +150,9 @@ bool MethodProvider::RegisterMethodHandlerWithInArgsOnly(const std::in template bool MethodProvider::RegisterMethodHandlerWithReturnOnly(const std::int32_t expected_return_value) { - auto handler_with_return_only = [expected_return_value]() -> std::int32_t { + auto handler_with_return_only = [expected_return_value](std::int32_t& return_value) { std::cout << "Provider: with_return_only called. Returning " << expected_return_value << std::endl; - return expected_return_value; + return_value = expected_return_value; }; const auto register_result = skeleton_->with_return_only.RegisterHandler(std::move(handler_with_return_only)); if (!register_result) diff --git a/score/mw/com/test/methods/non_trivial_constructors/provider.cpp b/score/mw/com/test/methods/non_trivial_constructors/provider.cpp index 5ee46c003..094794383 100644 --- a/score/mw/com/test/methods/non_trivial_constructors/provider.cpp +++ b/score/mw/com/test/methods/non_trivial_constructors/provider.cpp @@ -31,10 +31,11 @@ const std::string kInstanceSpecifier{"test/methods/non_trivial_constructors/Meth bool RegisterMethodHandlerWithInArgsAndReturn(NonTrivialConstructorSkeleton& skeleton) { - auto handler_with_in_args_and_return = [](NonTriviallyConstructibleType a, - NonTriviallyConstructibleType b) -> NonTriviallyConstructibleType { + auto handler_with_in_args_and_return = [](NonTriviallyConstructibleType& return_value, + const NonTriviallyConstructibleType& a, + const NonTriviallyConstructibleType& b) { std::cout << "Provider: with_in_args_and_return called with " << a << " + " << b << std::endl; - return a + b; + return_value = a + b; }; const auto register_result = skeleton.with_in_args_and_return.RegisterHandler(std::move(handler_with_in_args_and_return)); @@ -50,7 +51,8 @@ bool RegisterMethodHandlerWithInArgsAndReturn(NonTrivialConstructorSkeleton& ske bool RegisterMethodHandlerWithInArgsOnly(NonTrivialConstructorSkeleton& skeleton) { - auto handler_with_in_args_only = [](NonTriviallyConstructibleType a, NonTriviallyConstructibleType b) { + auto handler_with_in_args_only = [](const NonTriviallyConstructibleType& a, + const NonTriviallyConstructibleType& b) { std::cout << "Provider: with_in_args_only called with " << a << " + " << b << std::endl; SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(a == NonTriviallyConstructibleType{}, "Unexpected first InArg received!"); @@ -70,9 +72,9 @@ bool RegisterMethodHandlerWithInArgsOnly(NonTrivialConstructorSkeleton& skeleton bool RegisterMethodHandlerWithReturnOnly(NonTrivialConstructorSkeleton& skeleton) { - auto handler_with_return_only = []() -> NonTriviallyConstructibleType { + auto handler_with_return_only = [](NonTriviallyConstructibleType& return_value) { std::cout << "Provider: with_return_only called. Returning " << NonTriviallyConstructibleType{} << std::endl; - return NonTriviallyConstructibleType{}; + return_value = NonTriviallyConstructibleType{}; }; const auto register_result = skeleton.with_return_only.RegisterHandler(std::move(handler_with_return_only)); if (!register_result)