1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef IAM_UNITTEST_C_MOCKER_H
17 #define IAM_UNITTEST_C_MOCKER_H
18 
19 #include <dlfcn.h>
20 #include <mutex>
21 
22 #include <gmock/gmock.h>
23 
24 namespace OHOS {
25 namespace UserIam {
26 namespace UserAuth {
27 
28 template <typename T>
29 class CMocker {
30 public:
CMocker()31     CMocker()
32     {
33         std::lock_guard<std::mutex> lock(mutex_);
34         EXPECT_EQ(instance_, nullptr);
35         instance_ = static_cast<T *>(this);
36     }
37 
~CMocker()38     virtual ~CMocker()
39     {
40         std::lock_guard<std::mutex> lock(mutex_);
41         EXPECT_EQ(instance_, this);
42         instance_ = nullptr;
43     }
GetInstance()44     static inline T *GetInstance()
45     {
46         return instance_;
47     }
48 
GetMutex()49     static inline std::mutex &GetMutex()
50     {
51         return mutex_;
52     }
53 
54 private:
55     static T *instance_;
56     static std::mutex mutex_;
57 };
58 
59 template <typename T>
60 T *CMocker<T>::instance_ = nullptr;
61 
62 template <typename T>
63 std::mutex CMocker<T>::mutex_ {};
64 
65 #define SIGNATURE(ret, args) (GMOCK_INTERNAL_SIGNATURE(ret, args))
66 
67 #define PARAMETER(index, signature, dummy) \
68     GMOCK_PP_COMMA_IF(index) GMOCK_INTERNAL_ARG_O(index, GMOCK_PP_REMOVE_PARENS(signature))
69 
70 #define DECLARE_METHOD(ret, method, args)                                                                  \
71 public:                                                                                                    \
72     MOCK_METHOD(ret, method, args);                                                                        \
73     using typeof##method = ret (*)(GMOCK_PP_REPEAT(PARAMETER, SIGNATURE(ret, args), GMOCK_PP_NARG0 args)); \
74     using get##method = std::function<typeof##method()>;                                                   \
75     const static typeof##method default##method;
76 
77 #define IMPLEMENT_FUNCTION_INTERNAL(cls, method, count, signature, invoker)                               \
78     const cls::typeof##method cls::default##method = reinterpret_cast<cls::typeof##method>(invoker);      \
79     testing::internal::Function<GMOCK_PP_REMOVE_PARENS(signature)>::Result method(                        \
80         GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, signature, count))                                      \
81     {                                                                                                     \
82         const std::lock_guard<std::mutex> lock(cls::GetMutex());                                          \
83         static auto lookup = reinterpret_cast<cls::typeof##method>(dlsym(RTLD_NEXT, #method));            \
84                                                                                                           \
85         auto *mock = cls::GetInstance();                                                                  \
86         auto *stub = cls::default##method != nullptr ? cls::default##method : lookup;                     \
87                                                                                                           \
88         if (mock != nullptr && stub != nullptr) {                                                         \
89             ON_CALL(*mock, method).WillByDefault(stub);                                                   \
90         }                                                                                                 \
91                                                                                                           \
92         if (mock != nullptr) {                                                                            \
93             return mock->method(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, signature, count));           \
94         }                                                                                                 \
95                                                                                                           \
96         if (stub != nullptr) {                                                                            \
97             return stub(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, signature, count));                   \
98         }                                                                                                 \
99                                                                                                           \
100         testing::internal::Log(testing::internal::kWarning, #method " invoked without an implement.", 0); \
101         return testing::internal::Function<GMOCK_PP_REMOVE_PARENS(signature)>::Result();                  \
102     }
103 
104 #define IMPLEMENT_FUNCTION_WITH_INVOKER(cls, ret, method, args, invoker) \
105     IMPLEMENT_FUNCTION_INTERNAL(cls, method, GMOCK_PP_NARG0 args, SIGNATURE(ret, args), invoker)
106 
107 #define IMPLEMENT_FUNCTION(cls, ret, method, args) \
108     IMPLEMENT_FUNCTION_WITH_INVOKER(cls, ret, method, args, static_cast<void *>(nullptr))
109 
110 } // namespace UserAuth
111 } // namespace UserIam
112 } // namespace OHOS
113 
114 #endif // IAM_UNITTEST_C_MOCKER_H