/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #define ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #include #include #ifndef EGMOCK_VERBOSE #define EGMOCK_VERBOSE 0 #endif /** * Print log message. * * INTERNAL IMPLEMENTATION - don't use in user code. */ #if EGMOCK_VERBOSE #define EGMOCK_LOG_(...) LOG(VERBOSE) << "egmock: " << __VA_ARGS__ #else #define EGMOCK_LOG_(...) #endif /** * Common helper objects for gmock timeout extension. * * INTERNAL IMPLEMENTATION - don't use in user code. */ #define EGMOCK_TIMEOUT_METHOD_DEF_(Method, ...) \ std::atomic egmock_called_##Method; \ std::mutex egmock_mut_##Method; \ std::condition_variable egmock_cond_##Method; /** * Function similar to comma operator, to make it possible to return any value returned by mocked * function (which may be void) and discard the result of the other operation (notification about * a call). * * We need to invoke the mocked function (which result is returned) before the notification (which * result is dropped) - that's exactly the opposite of comma operator. * * INTERNAL IMPLEMENTATION - don't use in user code. */ template static T EGMockFlippedComma_(std::function returned, std::function discarded) { auto ret = returned(); discarded(); return ret; } template <> inline void EGMockFlippedComma_(std::function returned, std::function discarded) { returned(); discarded(); } /** * Common method body for gmock timeout extension. * * INTERNAL IMPLEMENTATION - don't use in user code. */ #define EGMOCK_TIMEOUT_METHOD_BODY_(Method, ...) \ auto invokeMock = [&]() { return egmock_##Method(__VA_ARGS__); }; \ auto notify = [&]() { \ std::lock_guard lk(egmock_mut_##Method); \ EGMOCK_LOG_(#Method " called"); \ egmock_called_##Method = true; \ egmock_cond_##Method.notify_all(); \ }; \ return EGMockFlippedComma_(invokeMock, notify); // We define this as a variadic macro in case F contains unprotected // commas (the same reason that we use variadic macros in other places // in this file). #define EGMOCK_RESULT_(tn, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::Result // The type of argument N of the given function type. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define EGMOCK_ARG_(tn, N, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::template Arg::type /** * Gmock MOCK_METHOD0 timeout-capable extension. */ #define MOCK_TIMEOUT_METHOD0(Method, ...) \ MOCK_METHOD0(egmock_##Method, __VA_ARGS__); \ EGMOCK_TIMEOUT_METHOD_DEF_(Method); \ virtual EGMOCK_RESULT_(, __VA_ARGS__) Method() { EGMOCK_TIMEOUT_METHOD_BODY_(Method); } /** * Gmock MOCK_METHOD1 timeout-capable extension. */ #define MOCK_TIMEOUT_METHOD1(Method, ...) \ MOCK_METHOD1(egmock_##Method, __VA_ARGS__); \ EGMOCK_TIMEOUT_METHOD_DEF_(Method); \ virtual EGMOCK_RESULT_(, __VA_ARGS__) Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1) { \ EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1); \ } /** * Gmock MOCK_METHOD2 timeout-capable extension. */ #define MOCK_TIMEOUT_METHOD2(Method, ...) \ MOCK_METHOD2(egmock_##Method, __VA_ARGS__); \ EGMOCK_TIMEOUT_METHOD_DEF_(Method); \ virtual EGMOCK_RESULT_(, __VA_ARGS__) \ Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1, EGMOCK_ARG_(, 2, __VA_ARGS__) egmock_a2) { \ EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1, egmock_a2); \ } /** * Gmock EXPECT_CALL timeout-capable extension. * * It has slightly different syntax from the original macro, to make method name accessible. * So, instead of typing * EXPECT_CALL(account, charge(100, Currency::USD)); * you need to inline arguments * EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD); */ #define EXPECT_TIMEOUT_CALL(obj, Method, ...) \ EGMOCK_LOG_(#Method " expected to call"); \ (obj).egmock_called_##Method = false; \ EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__)) /** * Waits for an earlier EXPECT_TIMEOUT_CALL to execute. * * It does not fully support special constraints of the EXPECT_CALL clause, just proceeds when the * first call to a given method comes. For example, in the following code: * EXPECT_TIMEOUT_CALL(account, charge, 100, _); * account.charge(50, Currency::USD); * EXPECT_TIMEOUT_CALL_WAIT(account, charge, 500ms); * the wait clause will just continue, as the charge method was called. * * @param obj object for a call * @param Method the method to wait for * @param timeout the maximum time for waiting */ #define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout) \ { \ EGMOCK_LOG_("waiting for " #Method " call"); \ std::unique_lock lk((obj).egmock_mut_##Method); \ if (!(obj).egmock_called_##Method) { \ auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \ EXPECT_EQ(std::cv_status::no_timeout, status); \ } \ } #endif // ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT