From 26b40e727923961cb374ebe3678d083bc99377bd Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Tue, 9 Feb 2021 13:48:44 +0000 Subject: [PATCH 1/5] Fix mbed_retarget.h stub on Mac OS. Some parts of the stat structure are already defined by the system as macros. The solution is to disable them. If these members are necessary for unit test, a deeper integration is required, the best being to use the native structure. --- UNITTESTS/target_h/platform/mbed_retarget.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UNITTESTS/target_h/platform/mbed_retarget.h b/UNITTESTS/target_h/platform/mbed_retarget.h index aea425ed7f..cd71083c03 100644 --- a/UNITTESTS/target_h/platform/mbed_retarget.h +++ b/UNITTESTS/target_h/platform/mbed_retarget.h @@ -374,9 +374,17 @@ struct stat { off_t st_size; ///< Size of file in bytes + // st_atime, st_mtime and st_ctime are defined as macros + // on platform like iOS. +#ifndef st_atime time_t st_atime; ///< Time of last access +#endif +#ifndef st_mtime time_t st_mtime; ///< Time of last data modification +#endif +#ifndef st_ctime time_t st_ctime; ///< Time of last status change +#endif }; #endif From dfe406b885aac98bf9a68958f29e20e15a337b19 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Tue, 9 Feb 2021 19:56:58 +0000 Subject: [PATCH 2/5] BLE: Add APIs to get characteristic authorization callbacks. --- .../include/ble/gatt/GattCharacteristic.h | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/connectivity/FEATURE_BLE/include/ble/gatt/GattCharacteristic.h b/connectivity/FEATURE_BLE/include/ble/gatt/GattCharacteristic.h index d5e88eedfb..cf310c3d4c 100644 --- a/connectivity/FEATURE_BLE/include/ble/gatt/GattCharacteristic.h +++ b/connectivity/FEATURE_BLE/include/ble/gatt/GattCharacteristic.h @@ -1579,6 +1579,17 @@ public: writeAuthorizationCallback.attach(object, member); } + /** + * Return the callback registered to handle client's write. + * + * @return the callback that handles client's write requests. + */ + const FunctionPointerWithContext& + getWriteAuthorizationCallback() const + { + return writeAuthorizationCallback; + } + /** * Register the read requests event handler. * @@ -1616,6 +1627,17 @@ public: readAuthorizationCallback.attach(object, member); } + /** + * Return the callback registered to handle client's read. + * + * @return the callback that handles client's read requests. + */ + const FunctionPointerWithContext& + getReadAuthorizationCallback() const + { + return readAuthorizationCallback; + } + /** * Invoke the write authorization callback. * From 4056fe092449ff5685e41717b598fbe388b76141 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Tue, 9 Feb 2021 19:43:33 +0000 Subject: [PATCH 3/5] BLE: Add API to test the presence of an event handler in a chain. --- .../ble/common/ChainableEventHandler.h | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/connectivity/FEATURE_BLE/include/ble/common/ChainableEventHandler.h b/connectivity/FEATURE_BLE/include/ble/common/ChainableEventHandler.h index 7e3f06237d..616980154b 100644 --- a/connectivity/FEATURE_BLE/include/ble/common/ChainableEventHandler.h +++ b/connectivity/FEATURE_BLE/include/ble/common/ChainableEventHandler.h @@ -71,14 +71,14 @@ public: * * @param[in] event_handler Pointer to event handler to remove */ - void removeEventHandler(T* target) { + void removeEventHandler(T* event_handler) { node_t* to_remove = head; - if(head->eh == target) { + if(head->eh == event_handler) { head = head->next; } else { auto* it = head; while(it->next) { - if(it->next->eh == target) { + if(it->next->eh == event_handler) { to_remove = it->next; break; } @@ -94,6 +94,22 @@ public: delete to_remove; } + /** + * Test if an event handler is present in the chain or not. + * + * @param[in] event_handler Pointer to event handler to check + */ + bool isEventHandlerPresent(T* event_handler) { + auto* it = head; + while (it) { + if (it == event_handler) { + return true; + } + it = it->next; + } + return false; + } + protected: template From 0feaae6d1862420ca3858c48ea1ab9bdf4c98dbc Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Tue, 9 Feb 2021 14:39:17 +0000 Subject: [PATCH 4/5] Add fake implementation for GattServerMock::addService. It is not easy to write expectations for this function: - The data in input is a complex tree that is not comparable. - The data in input is modified: The attributes of each element of the tree is added by the server. To ease test, a default implementation has been added but it is still possible to override it or set expectations. That default implementation set unique attribute handles for each element of the service and _copy_ it in the GattServerMock. The copy does *not* follow standard BLE API, it is a _POD_ where each element of the object is accessible as a field. Childs are stored in vector to ease iteration and memory management. The representation of the services registered into the mock are accessible in GattServerMock::services. --- UNITTESTS/fakes/ble/CMakeLists.txt | 6 ++ UNITTESTS/fakes/ble/GattServerImpl_mock.h | 45 ++++++++- .../fakes/ble/source/GattServerImpl_mock.cpp | 96 +++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 UNITTESTS/fakes/ble/source/GattServerImpl_mock.cpp diff --git a/UNITTESTS/fakes/ble/CMakeLists.txt b/UNITTESTS/fakes/ble/CMakeLists.txt index 1c1b32d75f..0dd25e965f 100644 --- a/UNITTESTS/fakes/ble/CMakeLists.txt +++ b/UNITTESTS/fakes/ble/CMakeLists.txt @@ -24,6 +24,12 @@ target_sources(mbed-os-fakes-ble ${MBED_PATH}/connectivity/FEATURE_BLE/source/GattServer.cpp ${MBED_PATH}/connectivity/FEATURE_BLE/source/SecurityManager.cpp ${MBED_PATH}/UNITTESTS/fakes/ble/BLE.cpp + ${MBED_PATH}/UNITTESTS/fakes/ble/source/GattServerImpl_mock.cpp + ${MBED_PATH}/UNITTESTS/fakes/ble/ble_mocks.h + ${MBED_PATH}/UNITTESTS/fakes/ble/GapImpl_mock.h + ${MBED_PATH}/UNITTESTS/fakes/ble/GattClientImpl_mock.h + ${MBED_PATH}/UNITTESTS/fakes/ble/GattServerImpl_mock.h + ${MBED_PATH}/UNITTESTS/fakes/ble/SecurityManagerImpl_mock.h ) target_link_libraries(mbed-os-fakes-ble diff --git a/UNITTESTS/fakes/ble/GattServerImpl_mock.h b/UNITTESTS/fakes/ble/GattServerImpl_mock.h index 7ade69615f..bfafb52400 100644 --- a/UNITTESTS/fakes/ble/GattServerImpl_mock.h +++ b/UNITTESTS/fakes/ble/GattServerImpl_mock.h @@ -25,10 +25,10 @@ namespace ble { class GattServerMock : public ble::impl::GattServer { public: - GattServerMock() {}; + GattServerMock(); GattServerMock(const GattServerMock&) = delete; GattServerMock& operator=(const GattServerMock&) = delete; - virtual ~GattServerMock() {}; + virtual ~GattServerMock(); MOCK_METHOD(ble_error_t, reset, (ble::GattServer* server), (override)); MOCK_METHOD(void, setEventHandler, (EventHandler *handler), (override)); @@ -57,6 +57,47 @@ public: MOCK_METHOD(void, handleDataReadEvent, (const GattReadCallbackParams *params), (override)); MOCK_METHOD(void, handleEvent, (GattServerEvents::gattEvent_e type, ble::connection_handle_t connHandle, GattAttribute::Handle_t attributeHandle), (override)); MOCK_METHOD(void, handleDataSentEvent, (unsigned count), (override)); + + // Fake part + // Descriptor representation of a descriptor registered with ble::test::register_services + struct descriptor_t { + UUID uuid; + ble::attribute_handle_t handle; + ble::att_security_requirement_t read_security = ble::att_security_requirement_t::NONE; + ble::att_security_requirement_t write_security = ble::att_security_requirement_t::NONE; + bool is_readable; + bool is_writable; + std::vector value; // Use capacity to determine the max size. + }; + + // Characteristic representation of a characteristic registered with ble::test::register_services + struct characteristic_t { + UUID uuid; + ble::attribute_handle_t value_handle; + uint8_t properties; + ble::att_security_requirement_t read_security = ble::att_security_requirement_t::NONE; + ble::att_security_requirement_t write_security = ble::att_security_requirement_t::NONE; + ble::att_security_requirement_t update_security = ble::att_security_requirement_t::NONE; + FunctionPointerWithContext + read_cb; + FunctionPointerWithContext + write_cb; + bool has_variable_len; + std::vector value; // Use capacity to determine the max size. + std::vector descriptors; + }; + + // Service representation of a service registered with ble::test::register_services + struct service_t { + UUID uuid; + ble::attribute_handle_t handle; + std::vector characteristics; + }; + + void fake_register_services(GattService& gattService); + + std::vector services; + ble::attribute_handle_t current_handle = 1; }; } diff --git a/UNITTESTS/fakes/ble/source/GattServerImpl_mock.cpp b/UNITTESTS/fakes/ble/source/GattServerImpl_mock.cpp new file mode 100644 index 0000000000..74d94ef304 --- /dev/null +++ b/UNITTESTS/fakes/ble/source/GattServerImpl_mock.cpp @@ -0,0 +1,96 @@ +/* mbed Microcontroller Library + * Copyright (c) 2021 ARM Limited + * 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 "GattServerImpl_mock.h" + +namespace ble { + +GattServerMock::GattServerMock() +{ + ON_CALL(*this, addService).WillByDefault([this](GattService &service) { + // Fake registration, it populates the handles of the input and store its + // representation in the services field. + fake_register_services(service); + return BLE_ERROR_NONE; + }); +} + +GattServerMock::~GattServerMock() {}; + +void GattServerMock::fake_register_services(GattService& gattService) +{ + gattService.setHandle(current_handle++); + service_t result { + gattService.getUUID(), + gattService.getHandle() + }; + + for (size_t i = 0; i < gattService.getCharacteristicCount(); ++i) { + current_handle++; // Increment for the characteristic declaration handle + auto& ref = *gattService.getCharacteristic(i); + ref.getValueAttribute().setHandle(current_handle++); + + characteristic_t c; + c.uuid = ref.getValueAttribute().getUUID(); + c.value_handle = ref.getValueHandle(); + c.properties = ref.getProperties(); + c.read_security = ref.getReadSecurityRequirement(); + c.write_security = ref.getWriteSecurityRequirement(); + c.update_security = ref.getUpdateSecurityRequirement(); + c.read_cb = ref.getReadAuthorizationCallback(); + c.write_cb = ref.getWriteAuthorizationCallback(); + c.value.reserve(ref.getValueAttribute().getMaxLength()); + c.value.resize(ref.getValueAttribute().getLength()); + { + auto value_ptr = ref.getValueAttribute().getValuePtr(); + if (value_ptr) { + std::copy(value_ptr, value_ptr + c.value.size(), c.value.begin()); + } + } + c.has_variable_len = ref.getValueAttribute().hasVariableLength(); + + for (size_t j = 0; j < ref.getDescriptorCount(); ++j) { + auto& ref_desc = *ref.getDescriptor(j); + ref_desc.setHandle(current_handle++); + + descriptor_t d; + d.uuid = ref_desc.getUUID(); + d.handle = ref_desc.getHandle(); + d.read_security = ref_desc.getReadSecurityRequirement(); + d.write_security = ref_desc.getWriteSecurityRequirement(); + d.is_readable = ref_desc.isReadAllowed(); + d.is_writable = ref_desc.isWriteAllowed(); + d.value.reserve(ref_desc.getMaxLength()); + d.value.resize(ref_desc.getLength()); + { + auto value_ptr = ref_desc.getValuePtr(); + if (value_ptr) { + std::copy(value_ptr, value_ptr + d.value.size(), d.value.begin()); + } + } + + c.descriptors.push_back(d); + } + + result.characteristics.push_back(c); + } + + services.push_back(result); +} + + +} \ No newline at end of file From a3a507d9ca9695c57b2734aa04999a7fc225cbf2 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Tue, 9 Feb 2021 17:45:24 +0000 Subject: [PATCH 5/5] Fix fake event queue process event. The result of remove_if was fed into erase without checking something was actually removed. --- UNITTESTS/fakes/events/EventQueue.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/UNITTESTS/fakes/events/EventQueue.cpp b/UNITTESTS/fakes/events/EventQueue.cpp index ba4b739f08..4f937c240c 100644 --- a/UNITTESTS/fakes/events/EventQueue.cpp +++ b/UNITTESTS/fakes/events/EventQueue.cpp @@ -98,21 +98,22 @@ void EventQueue::process_events() { } /* dispatch all handlers that happen at this time */ - _handlers.erase( - std::remove_if( - _handlers.begin(), - _handlers.end(), - [earliest_tick](internal_event& element) -> bool { - if (earliest_tick >= element.tick) { - (*(element.handler))(); - return true; - } else { - return false; - } + auto found = std::remove_if( + _handlers.begin(), + _handlers.end(), + [earliest_tick](internal_event& element) -> bool { + if (earliest_tick >= element.tick) { + (*(element.handler))(); + return true; + } else { + return false; } - ), - _handlers.end() + } ); + + if (found != _handlers.end()) { + _handlers.erase(found, _handlers.end()); + } } }