diff --git a/UNITTESTS/drivers/PwmOut/test_pwmout.cpp b/UNITTESTS/drivers/PwmOut/test_pwmout.cpp new file mode 100644 index 0000000000..4ea807cc70 --- /dev/null +++ b/UNITTESTS/drivers/PwmOut/test_pwmout.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "drivers/PwmOut.h" + +using namespace mbed; + +class MbedPowerMgmtInterface { +public: + virtual ~MbedPowerMgmtInterface() {} + virtual void sleep_manager_unlock_deep_sleep_internal() = 0; + virtual void sleep_manager_lock_deep_sleep_internal() = 0; +}; + + +class MockMbedPowerMgmt : public MbedPowerMgmtInterface { +public: + virtual ~MockMbedPowerMgmt() {} + + MOCK_METHOD0(sleep_manager_unlock_deep_sleep_internal, void()); + MOCK_METHOD0(sleep_manager_lock_deep_sleep_internal, void()); + + static MockMbedPowerMgmt *get_instance() + { + if (_instance == nullptr) { + _instance = new MockMbedPowerMgmt(); + } + + ++_ref_counter; + + return _instance; + } + + static void release_instance() + { + --_ref_counter; + + if ((_instance != nullptr) && (_ref_counter == 0)) { + delete _instance; + _instance = nullptr; + } + } + + static void sleep_manager_unlock_deep_sleep() + { + // Do not increment the call count unless it's done by a test + // case. + if (_instance) { + _instance->sleep_manager_unlock_deep_sleep_internal(); + } + } + + static void sleep_manager_lock_deep_sleep() + { + // Do not increment the call count unless it's done by a test + // case. + if (_instance) { + _instance->sleep_manager_lock_deep_sleep_internal(); + } + } + + MockMbedPowerMgmt(MockMbedPowerMgmt const &) = delete; + void operator=(MockMbedPowerMgmt const &) = delete; +private: + MockMbedPowerMgmt() {} + static MockMbedPowerMgmt *_instance; + static int _ref_counter; +}; +MockMbedPowerMgmt *MockMbedPowerMgmt::_instance = nullptr; +int MockMbedPowerMgmt::_ref_counter = 0; + + +void sleep_manager_unlock_deep_sleep(void) +{ + MockMbedPowerMgmt::sleep_manager_unlock_deep_sleep(); +} + + +void sleep_manager_lock_deep_sleep(void) +{ + MockMbedPowerMgmt::sleep_manager_lock_deep_sleep(); +} + + +class TestPwmOut : public testing::Test { +protected: + PwmOut *pwm_obj; + MockMbedPowerMgmt *mbed_mgmt_mock; + + void SetUp() + { + pwm_obj = new PwmOut(PTC1); + // Create a mock object singleton after the PwmOut object + // instantiation so the sleep_manager_lock_deep_sleep call by the + // constructor does not increment the call count. + // Now it is ok because a test case is about to start. + mbed_mgmt_mock = MockMbedPowerMgmt::get_instance(); + } + + void TearDown() + { + // Destroy the mock object singleton before the PwmOut destruction + // because it will increment the sleep_manager_unlock_deep_sleep call + // count. + MockMbedPowerMgmt::release_instance(); + delete pwm_obj; + } +}; +// *INDENT-ON* + + +/** Test if the constructor locks deepsleep + Given an instantiated Pmw + When the constructor is called + Then the deep sleep lock is acquired + */ +TEST_F(TestPwmOut, test_constructor) +{ + using ::testing::Mock; + + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_lock_deep_sleep_internal() + ).Times(1); + + PwmOut pmw(PTC1); + + // Force gMock to verify a mock object singleton before it is destructed + // by the teardown + Mock::VerifyAndClearExpectations(mbed_mgmt_mock); + // Suppress the memory leak error. The mock singleton will be released and + // deleted in the TearDown call. + Mock::AllowLeak(mbed_mgmt_mock); +} + + +/** Test if the destructor unlocks deepsleep + Given an instantiated Pmw + When the destructor is called + Then the deep sleep lock is released + */ +TEST_F(TestPwmOut, test_destructor) +{ + using ::testing::Mock; + + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_unlock_deep_sleep_internal() + ).Times(1); + + PwmOut *pmw = new PwmOut(PTC1); + delete pmw; + + // Force gMock to verify a mock object singleton before it is destructed + // by the teardown + Mock::VerifyAndClearExpectations(mbed_mgmt_mock); + // Suppress the memory leak error. The mock singleton will be released and + // deleted in the TearDown call. + Mock::AllowLeak(mbed_mgmt_mock); +} + + +/** Test if `suspend` unlocks deepsleep + + Given an initialised Pmw with a deep sleep lock + When the instance is suspended + Then the deep sleep lock is released once + */ +TEST_F(TestPwmOut, test_suspend) +{ + using ::testing::Mock; + + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_unlock_deep_sleep_internal() + ).Times(1); + pwm_obj->suspend(); + pwm_obj->suspend(); + + // Force gMock to verify a mock object singleton before it is destructed + // by the teardown + Mock::VerifyAndClearExpectations(mbed_mgmt_mock); + // Suppress the memory leak error. The mock singleton will be released and + // deleted in the TearDown call. + Mock::AllowLeak(mbed_mgmt_mock); +} + + +/** Test if `resume` lock deepsleep + + Given an initialised Pmw in a suspended state + When the instance is resumed + Then the deep sleep lock is re-acquired once + */ +TEST_F(TestPwmOut, test_resume) +{ + using ::testing::Mock; + + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_lock_deep_sleep_internal() + ).Times(1); + + pwm_obj->suspend(); + pwm_obj->resume(); + pwm_obj->resume(); + + // Force gMock to verify a mock object singleton before it is destructed + // by the teardown + Mock::VerifyAndClearExpectations(mbed_mgmt_mock); + // Suppress the memory leak error. The mock singleton will be released and + // deleted in the TearDown call. + Mock::AllowLeak(mbed_mgmt_mock); +} + +/** Test if `suspend`/`resume` unlock/lock deepsleep multiple times + + Given an initialised Pmw + When the instance is suspended and then resumed + Then the deep sleep lock can be unlocked and then locked again and again + */ +TEST_F(TestPwmOut, test_multiple_suspend_resume) +{ + using ::testing::Mock; + + const int suspend_resume_max_cycle = 3; + + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_unlock_deep_sleep_internal() + ).Times(suspend_resume_max_cycle); + EXPECT_CALL( + *mbed_mgmt_mock, sleep_manager_lock_deep_sleep_internal() + ).Times(suspend_resume_max_cycle); + + for (int i = 0; i < suspend_resume_max_cycle; i++) { + pwm_obj->suspend(); + pwm_obj->resume(); + } + + // Force gMock to verify a mock object singleton before it is destructed + // by the teardown + Mock::VerifyAndClearExpectations(mbed_mgmt_mock); + // Suppress the memory leak error. The mock singleton will be released and + // deleted in the TearDown call. + Mock::AllowLeak(mbed_mgmt_mock); +} diff --git a/UNITTESTS/drivers/PwmOut/unittest.cmake b/UNITTESTS/drivers/PwmOut/unittest.cmake new file mode 100644 index 0000000000..475cf0ae94 --- /dev/null +++ b/UNITTESTS/drivers/PwmOut/unittest.cmake @@ -0,0 +1,28 @@ + +#################### +# UNIT TESTS +#################### +set(TEST_SUITE_NAME "PwmOut") + +# Add test specific include paths +set(unittest-includes ${unittest-includes} + . + ../hal +) + +# Source files +set(unittest-sources + ../drivers/source/PwmOut.cpp +) + +# Test files +set(unittest-test-sources + drivers/PwmOut/test_pwmout.cpp + stubs/mbed_critical_stub.c + stubs/mbed_assert_stub.c + stubs/pwmout_api_stub.c +) + +# defines +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEVICE_PWMOUT") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEVICE_PWMOUT") diff --git a/UNITTESTS/stubs/pwmout_api_stub.c b/UNITTESTS/stubs/pwmout_api_stub.c new file mode 100644 index 0000000000..1eae62b0ca --- /dev/null +++ b/UNITTESTS/stubs/pwmout_api_stub.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Arm Limited and affiliates. + * + * 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. + */ +#include "hal/pwmout_api.h" + +#if DEVICE_PWMOUT + +void pwmout_init(pwmout_t *obj, PinName pin) +{ +} + +void pwmout_free(pwmout_t *obj) +{ +} + +void pwmout_write(pwmout_t *obj, float percent) +{ +} + +float pwmout_read(pwmout_t *obj) +{ + return 0; +} + +void pwmout_period(pwmout_t *obj, float seconds) +{ +} + +void pwmout_period_ms(pwmout_t *obj, int ms) +{ +} + +void pwmout_period_us(pwmout_t *obj, int us) +{ +} + +void pwmout_pulsewidth(pwmout_t *obj, float seconds) +{ +} + +void pwmout_pulsewidth_ms(pwmout_t *obj, int ms) +{ +} + +void pwmout_pulsewidth_us(pwmout_t *obj, int us) +{ +} + +#endif // DEVICE_PWMOUT diff --git a/UNITTESTS/target_h/PinNames.h b/UNITTESTS/target_h/PinNames.h index 243c64fd14..f580d15ae6 100644 --- a/UNITTESTS/target_h/PinNames.h +++ b/UNITTESTS/target_h/PinNames.h @@ -34,6 +34,10 @@ typedef enum { } PinName; typedef enum { + PullNone = 0, + PullDown = 1, + PullUp = 2, + PullDefault = PullUp } PinMode; #ifdef __cplusplus diff --git a/UNITTESTS/target_h/objects.h b/UNITTESTS/target_h/objects.h index 5753bbdcd4..13f95e1992 100644 --- a/UNITTESTS/target_h/objects.h +++ b/UNITTESTS/target_h/objects.h @@ -18,6 +18,7 @@ #ifndef MBED_OBJECTS_H #define MBED_OBJECTS_H +#include #include "PeripheralNames.h" #include "PinNames.h" @@ -33,6 +34,12 @@ struct serial_s { int x; }; +struct pwmout_s { + int pwm_name; +}; + + + #include "gpio_object.h" #ifdef __cplusplus diff --git a/UNITTESTS/target_h/platform/mbed_power_mgmt.h b/UNITTESTS/target_h/platform/mbed_power_mgmt.h index d0475a10d7..5ebb18c095 100644 --- a/UNITTESTS/target_h/platform/mbed_power_mgmt.h +++ b/UNITTESTS/target_h/platform/mbed_power_mgmt.h @@ -21,6 +21,7 @@ */ #ifndef MBED_POWER_MGMT_H #define MBED_POWER_MGMT_H + extern void mock_system_reset(); MBED_NORETURN static inline void system_reset(void) @@ -28,5 +29,9 @@ MBED_NORETURN static inline void system_reset(void) mock_system_reset(); } +void sleep_manager_unlock_deep_sleep(void); + +void sleep_manager_lock_deep_sleep(void); + #endif diff --git a/drivers/source/PwmOut.cpp b/drivers/source/PwmOut.cpp index 2951bbe414..ef0c8821a8 100644 --- a/drivers/source/PwmOut.cpp +++ b/drivers/source/PwmOut.cpp @@ -37,7 +37,6 @@ PwmOut::PwmOut(PinName pin) : PwmOut::~PwmOut() { - MBED_ASSERT(!_initialized); PwmOut::deinit(); }