diff --git a/.github/.cSpellWords.txt b/.github/.cSpellWords.txt index 69262e59..c96ad30e 100644 --- a/.github/.cSpellWords.txt +++ b/.github/.cSpellWords.txt @@ -172,3 +172,7 @@ utest vect writev xlarge +DCMAKE +Wextra +Wsign +Werror \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2eb6775c..db439ed0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: echo -e "::group::${{ env.bashInfo }} Generate Coverage Report ${{ env.bashEnd }}" # Generate coverage report, excluding extra directories - lcov --rc lcov_branch_coverage=1 -r build/coverage.info -o build/coverage.info '*test*' '*CMakeCCompilerId*' '*mocks*' '*dependency*' + lcov --rc lcov_branch_coverage=1 -r build/coverage.info -o build/coverage.info echo "::endgroup::" lcov --rc lcov_branch_coverage=1 --list build/coverage.info diff --git a/README.md b/README.md index d9e013e7..4701ae46 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,28 @@ git submodule update --checkout --init --recursive test/unit-test/CMock 1. Go to the root directory of this repository. (Make sure that the **CMock** submodule is cloned as described [above](#checkout-cmock-submodule)) -1. Run the *cmake* command: `cmake -S test -B build` +1. Run the *cmake* command: + + For Linux machines: + ``` + cmake -S test -B build/ \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_CLONE_SUBMODULES=ON \ + -DUNITTEST=1 \ + -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Wsign-compare -Werror -DNDEBUG -DLIBRARY_LOG_LEVEL=LOG_DEBUG' + ``` + For Mac machines: + + ``` + cmake -S test -B build/ \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DBUILD_CLONE_SUBMODULES=ON \ + -DUNITTEST=1 \ + -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Wsign-compare -Werror -DNDEBUG -DLIBRARY_LOG_LEVEL=LOG_DEBUG' \ + -DCMAKE_C_STANDARD=99 + ``` 1. Run this command to build the library and unit tests: `make -C build all` diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md index 89882800..3a7516bb 100644 --- a/docs/doxygen/include/size_table.md +++ b/docs/doxygen/include/size_table.md @@ -19,8 +19,8 @@ core_mqtt.c (coreMQTT) -
4.1K
-
3.5K
+
4.9K
+
4.2K
core_mqtt_state.c (coreMQTT) @@ -29,12 +29,12 @@ core_mqtt_serializer.c (coreMQTT) -
2.8K
-
2.2K
+
2.9K
+
2.3K
Total estimates -
10.6K
-
8.7K
+
11.5K
+
9.5K
diff --git a/manifest.yml b/manifest.yml index 29f8b99b..8a047e4c 100644 --- a/manifest.yml +++ b/manifest.yml @@ -5,7 +5,7 @@ description: | license: "MIT" dependencies: - name : "coreMQTT" - version: "v2.3.1" + version: "86a5750bb31e05fa69ef3f4e2f5e69d9317fae44" license: "MIT" repository: type: "git" diff --git a/source/core_mqtt_agent.c b/source/core_mqtt_agent.c index 077848fe..cbc95983 100644 --- a/source/core_mqtt_agent.c +++ b/source/core_mqtt_agent.c @@ -710,7 +710,6 @@ static void mqttEventCallback( MQTTContext_t * pMqttContext, break; /* Any other packet type is invalid. */ - case MQTT_PACKET_TYPE_PINGRESP: default: LogError( ( "Unknown packet type received:(%02x).\n", pPacketInfo->type ) ); @@ -953,7 +952,6 @@ static bool validateParams( MQTTAgentCommandType_t commandType, ( pSubscribeArgs->numSubscriptions != 0U ) ); break; - case PUBLISH: default: /* Publish, does not need to be cast since we do not check it. */ ret = ( pParams != NULL ); diff --git a/source/dependency/coreMQTT b/source/dependency/coreMQTT index 2beef047..86a5750b 160000 --- a/source/dependency/coreMQTT +++ b/source/dependency/coreMQTT @@ -1 +1 @@ -Subproject commit 2beef04725328923e05e576b884212d53ec97af7 +Subproject commit 86a5750bb31e05fa69ef3f4e2f5e69d9317fae44 diff --git a/source/include/core_mqtt_agent_config_defaults.h b/source/include/core_mqtt_agent_config_defaults.h index a98382cf..e1e40f9e 100644 --- a/source/include/core_mqtt_agent_config_defaults.h +++ b/source/include/core_mqtt_agent_config_defaults.h @@ -41,6 +41,8 @@ #endif /* *INDENT-ON* */ +#include "core_mqtt_config_defaults.h" + /* MQTT_AGENT_DO_NOT_USE_CUSTOM_CONFIG allows building the MQTT library * without a custom config. If a custom config is provided, the * MQTT_AGENT_DO_NOT_USE_CUSTOM_CONFIG macro should be defined. */ diff --git a/test/unit-test/cmock_opaque_types.h b/test/unit-test/cmock_opaque_types.h new file mode 100644 index 00000000..4a9fd488 --- /dev/null +++ b/test/unit-test/cmock_opaque_types.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef CMOCK_OPAQUE_TYPES_H_ +#define CMOCK_OPAQUE_TYPES_H_ + +/* CMock does not support opaque types so needs concrete definitions for them. + * This file is included in CMock .c files. */ + +struct MQTTVec +{ + void * a; + int b; +}; + +#endif /* ifndef CMOCK_OPAQUE_TYPES_H_ */ diff --git a/test/unit-test/mqtt_agent_utest.c b/test/unit-test/mqtt_agent_utest.c index 78aa1bd6..f327cf0b 100644 --- a/test/unit-test/mqtt_agent_utest.c +++ b/test/unit-test/mqtt_agent_utest.c @@ -216,6 +216,16 @@ static bool stubReleaseCommand( MQTTAgentCommand_t * pCommandToRelease ) return true; } +/** + * @brief A mocked function to release an allocated command. + */ +static bool stubReleaseCommandFailed( MQTTAgentCommand_t * pCommandToRelease ) +{ + ( void ) pCommandToRelease; + commandReleaseCallCount++; + return false; +} + /** * @brief A mock publish callback function. */ @@ -802,6 +812,29 @@ void test_MQTTAgent_Ping_Command_Send_Failure( void ) TEST_ASSERT_EQUAL( PING, command.commandType ); } +/** + * @brief Test error case when a command cannot be enqueued and the release also fails. + */ +void test_MQTTAgent_Ping_Command_Send_Failure_Release_Failed( void ) +{ + MQTTAgentContext_t agentContext = { 0 }; + MQTTStatus_t mqttStatus; + MQTTAgentCommandInfo_t commandInfo = { 0 }; + MQTTAgentCommand_t command = { 0 }; + + setupAgentContext( &agentContext ); + + pCommandToReturn = &command; + agentContext.agentInterface.send = stubSendFail; + agentContext.agentInterface.releaseCommand = stubReleaseCommandFailed; + mqttStatus = MQTTAgent_Ping( &agentContext, &commandInfo ); + TEST_ASSERT_EQUAL( MQTTSendFailed, mqttStatus ); + /* Test that the command was released. */ + TEST_ASSERT_EQUAL_INT( 1, commandReleaseCallCount ); + /* Also test that the command was set. */ + TEST_ASSERT_EQUAL( PING, command.commandType ); +} + /** * @brief Test that an MQTTNoMemory error is returned when there * is no more space to store a pending acknowledgment for @@ -1577,6 +1610,31 @@ void test_MQTTAgent_CommandLoop_with_eventCallback( void ) TEST_ASSERT_EQUAL( 0, mqttAgentContext.pPendingAcks[ 0 ].packetId ); TEST_ASSERT_EQUAL( NULL, mqttAgentContext.pPendingAcks[ 0 ].pOriginalCommand ); + /* Invoking mqttEventCallback with MQTT_PACKET_TYPE_PUBACK packet type with release command failed. + * MQTT_PACKET_TYPE_PUBCOMP, MQTT_PACKET_TYPE_SUBACK, MQTT_PACKET_TYPE_UNSUBACK + * packet types code path will also be covered by this test case. */ + packetType = MQTT_PACKET_TYPE_PUBACK; + commandCompleteCallbackCount = 0; + + mqttAgentContext.pPendingAcks[ 0 ].packetId = 1U; + command.pCommandCompleteCallback = stubCompletionCallback; + mqttAgentContext.pPendingAcks[ 0 ].pOriginalCommand = &command; + + MQTTAgentCommand_Publish_ExpectAnyArgsAndReturn( MQTTSuccess ); + MQTTAgentCommand_Publish_ReturnThruPtr_pReturnFlags( &returnFlags ); + mqttAgentContext.agentInterface.releaseCommand = stubReleaseCommandFailed; + + MQTT_ProcessLoop_Stub( MQTT_ProcessLoop_CustomStub ); + + mqttStatus = MQTTAgent_CommandLoop( &mqttAgentContext ); + + TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus ); + /* Ensure that callback is invoked. */ + TEST_ASSERT_EQUAL( 2, commandCompleteCallbackCount ); + /* Ensure that acknowledgment is cleared. */ + TEST_ASSERT_EQUAL( 0, mqttAgentContext.pPendingAcks[ 0 ].packetId ); + TEST_ASSERT_EQUAL( NULL, mqttAgentContext.pPendingAcks[ 0 ].pOriginalCommand ); + /* mqttEventcallback behavior when the command for the pending ack is NULL for the received PUBACK. */ commandCompleteCallbackCount = 0; mqttAgentContext.pPendingAcks[ 0 ].packetId = 1U; diff --git a/tools/cmock/coverage.cmake b/tools/cmock/coverage.cmake index e035ea4c..e56e0417 100644 --- a/tools/cmock/coverage.cmake +++ b/tools/cmock/coverage.cmake @@ -15,8 +15,9 @@ execute_process( COMMAND lcov --directory ${CMAKE_BINARY_DIR} --initial --capture --rc lcov_branch_coverage=1 - --rc genhtml_branch_coverage=1 --output-file=${CMAKE_BINARY_DIR}/base_coverage.info + --include "*source*" + ) file(GLOB files "${CMAKE_BINARY_DIR}/bin/tests/*") @@ -46,10 +47,10 @@ execute_process(COMMAND ruby execute_process( COMMAND lcov --capture --rc lcov_branch_coverage=1 - --rc genhtml_branch_coverage=1 --base-directory ${CMAKE_BINARY_DIR} --directory ${CMAKE_BINARY_DIR} --output-file ${CMAKE_BINARY_DIR}/second_coverage.info + --include "*source*" ) # combile baseline results (zeros) with the one after running the tests @@ -59,8 +60,8 @@ execute_process( --add-tracefile ${CMAKE_BINARY_DIR}/base_coverage.info --add-tracefile ${CMAKE_BINARY_DIR}/second_coverage.info --output-file ${CMAKE_BINARY_DIR}/coverage.info - --no-external --rc lcov_branch_coverage=1 + --include "*source*" ) execute_process( COMMAND genhtml --rc lcov_branch_coverage=1 diff --git a/tools/cmock/create_test.cmake b/tools/cmock/create_test.cmake index 24b7e56f..60acc70b 100644 --- a/tools/cmock/create_test.cmake +++ b/tools/cmock/create_test.cmake @@ -45,7 +45,7 @@ function(create_test test_name add_dependencies(${test_name} ${dependency}) target_link_libraries(${test_name} ${dependency}) endforeach() - target_link_libraries(${test_name} -lgcov unity) + target_link_libraries(${test_name} unity) target_link_directories(${test_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib ) @@ -129,11 +129,20 @@ function(create_mock_list mock_name ${mocks_dir} ${mock_include_list} ) - set_target_properties(${mock_name} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib - POSITION_INDEPENDENT_CODE ON - ) - target_compile_definitions(${mock_name} PUBLIC + if (APPLE) + set_target_properties(${mock_name} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib + POSITION_INDEPENDENT_CODE ON + LINK_FLAGS "-Wl,-undefined,dynamic_lookup" + ) + else() + set_target_properties(${mock_name} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib + POSITION_INDEPENDENT_CODE ON + ) + endif() + + target_compile_definitions(${mock_name} PUBLIC ${mock_define_list} ) target_link_libraries(${mock_name} cmock unity) @@ -160,9 +169,5 @@ function(create_real_library target ) if(NOT(mock_name STREQUAL "")) add_dependencies(${target} ${mock_name}) - target_link_libraries(${target} - -l${mock_name} - -lgcov - ) endif() endfunction() diff --git a/tools/cmock/project.yml b/tools/cmock/project.yml index 8a90416b..b243af6e 100644 --- a/tools/cmock/project.yml +++ b/tools/cmock/project.yml @@ -21,6 +21,7 @@ :includes: # This will add these includes to each mock. - - + :includes_c_post_header: + - :treat_externs: :exclude # Now the extern-ed functions will be mocked. - :weak: __attribute__((weak)) :treat_externs: :include