diff --git a/configure.ac b/configure.ac index f662e59c5f18fb..e1444bbf0f1405 100644 --- a/configure.ac +++ b/configure.ac @@ -1812,6 +1812,7 @@ src/setup_payload/tests/Makefile src/inet/Makefile src/inet/tests/Makefile src/lib/Makefile +src/lib/support/Makefile src/platform/Makefile tests/Makefile ]) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 33239023c8166c..d28251da935301 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -23,6 +23,10 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am +SUBDIRS = \ + support \ + $(NULL) + # Pull in the sources that comprise the CHIP library. include ../system/SystemLayer.am @@ -31,6 +35,10 @@ include ../ble/BleLayer.am include core/CoreLayer.am include support/SupportLayer.am +EXTRA_DIST = \ + $(CHIP_BUILD_CORE_LAYER_HEADER_FILES) \ + $(CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES) + lib_LIBRARIES = libCHIP.a libCHIP_a_CPPFLAGS = \ @@ -54,8 +62,4 @@ if CONFIG_NETWORK_LAYER_BLE libCHIP_a_SOURCES += $(CHIP_BUILD_BLE_LAYER_SOURCE_FILES) endif # CONFIG_NETWORK_LAYER_BLE -EXTRA_DIST = \ - $(CHIP_BUILD_CORE_LAYER_HEADER_FILES) \ - $(CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES) - include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 74d712f955b8d8..f24872747f1090 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -36,7 +36,7 @@ #ifndef CHIP_CONFIG_H_ #define CHIP_CONFIG_H_ -#include +#include /* COMING SOON: making the INET Layer optional entails making this inclusion optional. */ //#include "InetConfig.h" diff --git a/src/lib/support/Base64.h b/src/lib/support/Base64.h index ac7484e7bf1f52..69ffb930e041a0 100644 --- a/src/lib/support/Base64.h +++ b/src/lib/support/Base64.h @@ -38,9 +38,9 @@ typedef uint8_t (*Base64CharToValFunct)(uint8_t c); // Output buffer must be at least (inLen + 2) / 3 * 4 bytes long. // Input and output buffers CANNOT overlap. // -extern uint16_t Base64Encode(const uint8_t *in, uint16_t inLen, char *out); -extern uint16_t Base64URLEncode(const uint8_t *in, uint16_t inLen, char *out); -extern uint16_t Base64Encode(const uint8_t *in, uint16_t inLen, char *out, Base64ValToCharFunct valToCharFunct); +extern uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out); +extern uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out); +extern uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct); // Decode a base64 string to bytes. // @@ -50,18 +50,18 @@ extern uint16_t Base64Encode(const uint8_t *in, uint16_t inLen, char *out, Base6 // may be shorter than this due to padding. // Supports decode in place by setting out pointer equal to in. // -extern uint16_t Base64Decode(const char *in, uint16_t inLen, uint8_t *out); -extern uint16_t Base64URLDecode(const char *in, uint16_t inLen, uint8_t *out); -extern uint16_t Base64Decode(const char *in, uint16_t inLen, uint8_t *out, Base64CharToValFunct charToValFunct); +extern uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out); +extern uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out); +extern uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct); // Encode/decode functions that take/return 32-bit lengths. // // Similar to the above functions, except Base64Decode32() returns UINT32_MAX if the input cannot be decoded. // -extern uint32_t Base64Encode32(const uint8_t *in, uint32_t inLen, char *out); -extern uint32_t Base64Encode32(const uint8_t *in, uint32_t inLen, char *out, Base64ValToCharFunct valToCharFunct); -extern uint32_t Base64Decode32(const char *in, uint32_t inLen, uint8_t *out); -extern uint32_t Base64Decode32(const char *in, uint32_t inLen, uint8_t *out, Base64CharToValFunct charToValFunct); +extern uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out); +extern uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct); +extern uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out); +extern uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct); /** Computes the base-64 encoded length for a given input length. * @@ -75,8 +75,7 @@ extern uint32_t Base64Decode32(const char *in, uint32_t inLen, uint8_t *out, Bas * * NOTE: The actual decoded length may be smaller than this due to padding. */ -#define BASE64_MAX_DECODED_LEN(LEN) ((LEN) * 3 / 4) - +#define BASE64_MAX_DECODED_LEN(LEN) ((LEN) *3 / 4) } // namespace chip diff --git a/src/lib/support/CHIPFaultInjection.h b/src/lib/support/CHIPFaultInjection.h index cb2ac156c2d767..d995c938071da5 100644 --- a/src/lib/support/CHIPFaultInjection.h +++ b/src/lib/support/CHIPFaultInjection.h @@ -47,60 +47,60 @@ namespace FaultInjection { */ typedef enum { - kFault_AllocExchangeContext, /**< Fail the allocation of an ExchangeContext */ - kFault_DropIncomingUDPMsg, /**< Drop an incoming UDP message without any processing */ - kFault_DropOutgoingUDPMsg, /**< Drop an outgoing UDP message at the chip Message layer */ - kFault_AllocBinding, /**< Fail the allocation of a Binding */ - kFault_SendAlarm, /**< Fail to send an alarm message */ - kFault_HandleAlarm, /**< Fail to handle an alarm message */ - kFault_FuzzExchangeHeaderTx, /**< Fuzz a chip Exchange Header after it has been encoded into the packet buffer; - when the fault is enabled, it expects an integer argument, which is an index into - a table of modifications that can be applied to the header. @see FuzzExchangeHeader */ + kFault_AllocExchangeContext, /**< Fail the allocation of an ExchangeContext */ + kFault_DropIncomingUDPMsg, /**< Drop an incoming UDP message without any processing */ + kFault_DropOutgoingUDPMsg, /**< Drop an outgoing UDP message at the chip Message layer */ + kFault_AllocBinding, /**< Fail the allocation of a Binding */ + kFault_SendAlarm, /**< Fail to send an alarm message */ + kFault_HandleAlarm, /**< Fail to handle an alarm message */ + kFault_FuzzExchangeHeaderTx, /**< Fuzz a chip Exchange Header after it has been encoded into the packet buffer; + when the fault is enabled, it expects an integer argument, which is an index into + a table of modifications that can be applied to the header. @see FuzzExchangeHeader */ #if CHIP_CONFIG_ENABLE_RELIABLE_MESSAGING - kFault_WRMDoubleTx, /**< Force WRMP to transmit the outgoing message twice */ - kFault_WRMSendError, /**< Fail a transmission in WRMP as if the max number of retransmission has been exceeded */ -#endif // CHIP_CONFIG_ENABLE_RELIABLE_MESSAGING - kFault_BDXBadBlockCounter, /**< Corrupt the BDX Block Counter in the BDX BlockSend or BlockEOF message about to be sent */ - kFault_BDXAllocTransfer, /**< Fail the allocation of a BDXTransfer object */ + kFault_WRMDoubleTx, /**< Force WRMP to transmit the outgoing message twice */ + kFault_WRMSendError, /**< Fail a transmission in WRMP as if the max number of retransmission has been exceeded */ +#endif // CHIP_CONFIG_ENABLE_RELIABLE_MESSAGING + kFault_BDXBadBlockCounter, /**< Corrupt the BDX Block Counter in the BDX BlockSend or BlockEOF message about to be sent */ + kFault_BDXAllocTransfer, /**< Fail the allocation of a BDXTransfer object */ #if CHIP_CONFIG_ENABLE_SERVICE_DIRECTORY - kFault_ServiceManager_ConnectRequestNew, /**< Fail the allocation of a chipServiceManager::ConnectRequest */ - kFault_ServiceManager_Lookup, /**< Fail the lookup of an endpoint id */ - kFault_ServiceDirectoryReplaceError, /**< Fail the replacement of a ServiceDirectory entry */ -#endif // CHIP_CONFIG_ENABLE_SERVICE_DIRECTORY - kFault_WDM_TraitInstanceNew, /**< Fail the allocation of a WDM TraitInstanceInfo object */ - kFault_WDM_SubscriptionHandlerNew, /**< Fail the allocation of a WDM SubscriptionHandler object */ - kFault_WDM_SubscriptionClientNew, /**< Fail the allocation of a WDM SubscriptionClient object */ - kFault_WDM_BadSubscriptionId, /**< Corrupt the SubscriptionId of an incoming notification */ - kFault_WDM_SendUnsupportedReqMsgType, /**< Corrupt the message type of an outgoing SubscriptionRequest, so it is received as an unsupported - message by the responder */ - kFault_WDM_NotificationSize, /**< Override the max payload size in a SubscriptionHandler; the size to be used can passed as - an argument to the fault */ - kFault_WDM_SendCommandExpired, /**< Force the ExpiryTime of a WDM command to be in the past */ - kFault_WDM_SendCommandBadVersion, /**< Alter the version of a WDM command being transmitted */ - kFault_WDM_SendUpdateBadVersion, /**< Alter the version of a WDM update data element being transmitted */ - kFault_WDM_DelayUpdateResponse, /**< Drop the message received after sending an UpdateRequest, which usually is the StatusReport; - this causes the NotificationRequest to be processed first */ - kFault_WDM_UpdateRequestTimeout, /**< Inject an exchange timeout for the UpdateRequest */ - kFault_WDM_UpdateRequestSendErrorInline, /**< Inject an inline Inet Send error for the UpdateRequest */ - kFault_WDM_UpdateRequestSendErrorAsync, /**< Inject a WRM SendError for the UpdateRequest */ - kFault_WDM_UpdateRequestBadProfile, /**< Inject an invalid Profile ID in the UpdateRequest */ - kFault_WDM_UpdateRequestDropMessage, /**< Drop an outgoing WDM UpdateRequest message using the DropOutgoingUDPMsg fault */ - kFault_WDM_UpdateResponseBusy, /**< Inject a status code busy in the StatusList */ - kFault_WDM_PathStoreFull, /**< Inject a WDM_PATH_STORE_FULL error */ - kFault_WDM_TreatNotifyAsCancel, /**< Process a Notify request as a CancelSubscription request */ - kFault_CASEKeyConfirm, /**< Trigger a CHIP_ERROR_KEY_CONFIRMATION_FAILED error in chipCASEEngine */ - kFault_SecMgrBusy, /**< Trigger a CHIP_ERROR_SECURITY_MANAGER_BUSY when starting an authentication session */ + kFault_ServiceManager_ConnectRequestNew, /**< Fail the allocation of a chipServiceManager::ConnectRequest */ + kFault_ServiceManager_Lookup, /**< Fail the lookup of an endpoint id */ + kFault_ServiceDirectoryReplaceError, /**< Fail the replacement of a ServiceDirectory entry */ +#endif // CHIP_CONFIG_ENABLE_SERVICE_DIRECTORY + kFault_WDM_TraitInstanceNew, /**< Fail the allocation of a WDM TraitInstanceInfo object */ + kFault_WDM_SubscriptionHandlerNew, /**< Fail the allocation of a WDM SubscriptionHandler object */ + kFault_WDM_SubscriptionClientNew, /**< Fail the allocation of a WDM SubscriptionClient object */ + kFault_WDM_BadSubscriptionId, /**< Corrupt the SubscriptionId of an incoming notification */ + kFault_WDM_SendUnsupportedReqMsgType, /**< Corrupt the message type of an outgoing SubscriptionRequest, so it is received as an + unsupported message by the responder */ + kFault_WDM_NotificationSize, /**< Override the max payload size in a SubscriptionHandler; the size to be used can passed as + an argument to the fault */ + kFault_WDM_SendCommandExpired, /**< Force the ExpiryTime of a WDM command to be in the past */ + kFault_WDM_SendCommandBadVersion, /**< Alter the version of a WDM command being transmitted */ + kFault_WDM_SendUpdateBadVersion, /**< Alter the version of a WDM update data element being transmitted */ + kFault_WDM_DelayUpdateResponse, /**< Drop the message received after sending an UpdateRequest, which usually is the + StatusReport; this causes the NotificationRequest to be processed first */ + kFault_WDM_UpdateRequestTimeout, /**< Inject an exchange timeout for the UpdateRequest */ + kFault_WDM_UpdateRequestSendErrorInline, /**< Inject an inline Inet Send error for the UpdateRequest */ + kFault_WDM_UpdateRequestSendErrorAsync, /**< Inject a WRM SendError for the UpdateRequest */ + kFault_WDM_UpdateRequestBadProfile, /**< Inject an invalid Profile ID in the UpdateRequest */ + kFault_WDM_UpdateRequestDropMessage, /**< Drop an outgoing WDM UpdateRequest message using the DropOutgoingUDPMsg fault */ + kFault_WDM_UpdateResponseBusy, /**< Inject a status code busy in the StatusList */ + kFault_WDM_PathStoreFull, /**< Inject a WDM_PATH_STORE_FULL error */ + kFault_WDM_TreatNotifyAsCancel, /**< Process a Notify request as a CancelSubscription request */ + kFault_CASEKeyConfirm, /**< Trigger a CHIP_ERROR_KEY_CONFIRMATION_FAILED error in chipCASEEngine */ + kFault_SecMgrBusy, /**< Trigger a CHIP_ERROR_SECURITY_MANAGER_BUSY when starting an authentication session */ #if CHIP_CONFIG_ENABLE_TUNNELING - kFault_TunnelQueueFull, /**< Trigger a CHIP_ERROR_TUNNEL_SERVICE_QUEUE_FULL when enqueueing a packet in the Tunnel queue */ - kFault_TunnelPacketDropByPolicy, /**< Trigger an explicit drop of the packet as if done by an application policy */ -#endif // CHIP_CONFIG_ENABLE_TUNNELING + kFault_TunnelQueueFull, /**< Trigger a CHIP_ERROR_TUNNEL_SERVICE_QUEUE_FULL when enqueueing a packet in the Tunnel queue */ + kFault_TunnelPacketDropByPolicy, /**< Trigger an explicit drop of the packet as if done by an application policy */ +#endif // CHIP_CONFIG_ENABLE_TUNNELING #if CONFIG_NETWORK_LAYER_BLE - kFault_CHIPOBLESend, /**< Inject a GATT error when sending the first fragment of a chip message over BLE */ -#endif // CONFIG_NETWORK_LAYER_BLE + kFault_CHIPOBLESend, /**< Inject a GATT error when sending the first fragment of a chip message over BLE */ +#endif // CONFIG_NETWORK_LAYER_BLE kFault_NumItems, } Id; -DLL_EXPORT nl::FaultInjection::Manager &GetManager(void); +DLL_EXPORT nl::FaultInjection::Manager & GetManager(void); /** * The number of ways in which chip Fault Injection fuzzers can @@ -108,7 +108,7 @@ DLL_EXPORT nl::FaultInjection::Manager &GetManager(void); */ #define CHIP_FAULT_INJECTION_NUM_FUZZ_VALUES 3 -DLL_EXPORT void FuzzExchangeHeader(uint8_t *p, int32_t arg); +DLL_EXPORT void FuzzExchangeHeader(uint8_t * p, int32_t arg); } // namespace FaultInjection } // namespace chip @@ -120,8 +120,7 @@ DLL_EXPORT void FuzzExchangeHeader(uint8_t *p, int32_t arg); * @param[in] aFaultID A chip fault-injection id * @param[in] aStatements Statements to be executed if the fault is enabled. */ -#define CHIP_FAULT_INJECT( aFaultID, aStatements ) \ - nlFAULT_INJECT(chip::FaultInjection::GetManager(), aFaultID, aStatements) +#define CHIP_FAULT_INJECT(aFaultID, aStatements) nlFAULT_INJECT(chip::FaultInjection::GetManager(), aFaultID, aStatements) /** * Execute the statements included if the chip fault is @@ -136,16 +135,17 @@ DLL_EXPORT void FuzzExchangeHeader(uint8_t *p, int32_t arg); * @param[in] aUnprotectedStatements Statements to be executed if the fault is enabled without holding the * Manager's lock */ -#define CHIP_FAULT_INJECT_MAX_ARG( aFaultID, aMaxArg, aProtectedStatements, aUnprotectedStatements ) \ - do { \ - FaultInjection::Manager &mgr = chip::FaultInjection::GetManager(); \ - const FaultInjection::Record *records = mgr.GetFaultRecords(); \ - if (records[aFaultID].mNumArguments == 0) \ - { \ - int32_t arg = aMaxArg; \ - mgr.StoreArgsAtFault(aFaultID, 1, &arg); \ - } \ - nlFAULT_INJECT_WITH_ARGS(mgr, aFaultID, aProtectedStatements, aUnprotectedStatements ); \ +#define CHIP_FAULT_INJECT_MAX_ARG(aFaultID, aMaxArg, aProtectedStatements, aUnprotectedStatements) \ + do \ + { \ + FaultInjection::Manager & mgr = chip::FaultInjection::GetManager(); \ + const FaultInjection::Record * records = mgr.GetFaultRecords(); \ + if (records[aFaultID].mNumArguments == 0) \ + { \ + int32_t arg = aMaxArg; \ + mgr.StoreArgsAtFault(aFaultID, 1, &arg); \ + } \ + nlFAULT_INJECT_WITH_ARGS(mgr, aFaultID, aProtectedStatements, aUnprotectedStatements); \ } while (0) /** @@ -158,20 +158,18 @@ DLL_EXPORT void FuzzExchangeHeader(uint8_t *p, int32_t arg); * @param[in] aUnprotectedStatements Statements to be executed if the fault is enabled without holding the * Manager's lock */ -#define CHIP_FAULT_INJECT_WITH_ARGS( aFaultID, aProtectedStatements, aUnprotectedStatements ) \ - nlFAULT_INJECT_WITH_ARGS(chip::FaultInjection::GetManager(), aFaultID, \ - aProtectedStatements, aUnprotectedStatements ); +#define CHIP_FAULT_INJECT_WITH_ARGS(aFaultID, aProtectedStatements, aUnprotectedStatements) \ + nlFAULT_INJECT_WITH_ARGS(chip::FaultInjection::GetManager(), aFaultID, aProtectedStatements, aUnprotectedStatements); #define CHIP_FAULT_INJECTION_EXCH_HEADER_NUM_FIELDS 4 #define CHIP_FAULT_INJECTION_EXCH_HEADER_NUM_FIELDS_WRMP 5 #else // CHIP_CONFIG_TEST -#define CHIP_FAULT_INJECT( aFaultID, aStatements ) -#define CHIP_FAULT_INJECT_WITH_ARGS( aFaultID, aProtectedStatements, aUnprotectedStatements ) -#define CHIP_FAULT_INJECT_MAX_ARG( aFaultID, aMaxArg, aProtectedStatements, aUnprotectedStatements ) +#define CHIP_FAULT_INJECT(aFaultID, aStatements) +#define CHIP_FAULT_INJECT_WITH_ARGS(aFaultID, aProtectedStatements, aUnprotectedStatements) +#define CHIP_FAULT_INJECT_MAX_ARG(aFaultID, aMaxArg, aProtectedStatements, aUnprotectedStatements) #endif // CHIP_CONFIG_TEST - #endif // CHIP_FAULT_INJECTION_H_ diff --git a/src/lib/support/CodeUtils.h b/src/lib/support/CodeUtils.h index 8f13ed33be79f6..e8a42675c0d18b 100644 --- a/src/lib/support/CodeUtils.h +++ b/src/lib/support/CodeUtils.h @@ -47,8 +47,7 @@ * */ #if !defined(CHIP_ASSERT_ABORT) -#define CHIP_ASSERT_ABORT() \ - chipDie() +#define CHIP_ASSERT_ABORT() chipDie() #endif /** @@ -91,6 +90,7 @@ * further describing the assertion failure. * */ +// clang-format off #if !defined(CHIP_ASSERT_LOG) #define CHIP_ASSERT_LOG(aPrefix, aName, aCondition, aLabel, aFile, aLine, aMessage) \ do \ @@ -107,6 +107,7 @@ aLine); \ } while (0) #endif +// clang-format on /** * @} chip-specific nlassert.h Overrides @@ -120,8 +121,7 @@ namespace chip { // Generic min() and max() functions // template -inline const _T & -min(const _T &a, const _T &b) +inline const _T & min(const _T & a, const _T & b) { if (b < a) return b; @@ -130,8 +130,7 @@ min(const _T &a, const _T &b) } template -inline const _T & -max(const _T &a, const _T &b) +inline const _T & max(const _T & a, const _T & b) { if (a < b) return b; @@ -156,8 +155,7 @@ max(const _T &a, const _T &b) * @endcode * */ -#define IgnoreUnusedVariable(aVariable) \ - ((void)(aVariable)) +#define IgnoreUnusedVariable(aVariable) ((void) (aVariable)) /** * @def SuccessOrExit(aStatus) @@ -188,8 +186,7 @@ max(const _T &a, const _T &b) * @param[in] aStatus A scalar status to be evaluated against zero (0). * */ -#define SuccessOrExit(aStatus) \ - nlEXPECT((aStatus) == CHIP_NO_ERROR, exit) +#define SuccessOrExit(aStatus) nlEXPECT((aStatus) == CHIP_NO_ERROR, exit) /** * @def VerifyOrExit(aCondition, anAction) @@ -221,8 +218,7 @@ max(const _T &a, const _T &b) * assertion fails. * */ -#define VerifyOrExit(aCondition, anAction) \ - nlEXPECT_ACTION(aCondition, exit, anAction) +#define VerifyOrExit(aCondition, anAction) nlEXPECT_ACTION(aCondition, exit, anAction) /** * @def ExitNow(...) @@ -260,11 +256,13 @@ max(const _T &a, const _T &b) * when the assertion fails. * */ +// clang-format off #define ExitNow(...) \ do { \ __VA_ARGS__; \ goto exit; \ } while (0) +// clang-format on /** * @brief @@ -320,8 +318,7 @@ inline void chipDie(void) * @sa #chipDie * */ -#define VerifyOrDie(aCondition) \ - nlABORT(aCondition) +#define VerifyOrDie(aCondition) nlABORT(aCondition) /** * @def VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) @@ -357,8 +354,8 @@ inline void chipDie(void) * @sa #chipDie * */ -#define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \ - nlABORT_ACTION(aCondition, ChipLogDetail(aModule, aMessage, ## __VA_ARGS__)) +#define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \ + nlABORT_ACTION(aCondition, ChipLogDetail(aModule, aMessage, ##__VA_ARGS__)) /** * @def ArraySize(aArray) @@ -375,7 +372,7 @@ inline void chipDie(void) * * @return The size of an array in number of elements. */ -#define ArraySize(a) (sizeof(a)/sizeof((a)[0])) +#define ArraySize(a) (sizeof(a) / sizeof((a)[0])) #if defined(__cplusplus) && (__cplusplus >= 201103L) diff --git a/src/lib/support/DLLUtil.h b/src/lib/support/DLLUtil.h index 23eac62efa6a92..7cd4b1e07f4ef3 100644 --- a/src/lib/support/DLLUtil.h +++ b/src/lib/support/DLLUtil.h @@ -30,19 +30,19 @@ #define DLLEXPORT_H_ #if defined _WIN32 || defined __CYGWIN__ - #define DLL_IMPORT __declspec(dllimport) - #define DLL_EXPORT __declspec(dllexport) - #define DLL_LOCAL +#define DLL_IMPORT __declspec(dllimport) +#define DLL_EXPORT __declspec(dllexport) +#define DLL_LOCAL #else - #if __GNUC__ >= 4 - #define DLL_IMPORT __attribute__ ((visibility ("default"))) - #define DLL_EXPORT __attribute__ ((visibility ("default"))) - #define DLL_LOCAL __attribute__ ((visibility ("hidden"))) - #else - #define DLL_IMPORT - #define DLL_EXPORT - #define DLL_LOCAL - #endif +#if __GNUC__ >= 4 +#define DLL_IMPORT __attribute__((visibility("default"))) +#define DLL_EXPORT __attribute__((visibility("default"))) +#define DLL_LOCAL __attribute__((visibility("hidden"))) +#else +#define DLL_IMPORT +#define DLL_EXPORT +#define DLL_LOCAL +#endif #endif #endif /* DLLEXPORT_H_ */ diff --git a/src/lib/support/FlagUtils.hpp b/src/lib/support/FlagUtils.hpp index 51cd38b4e8c141..86f4ddffea2f6b 100644 --- a/src/lib/support/FlagUtils.hpp +++ b/src/lib/support/FlagUtils.hpp @@ -30,26 +30,26 @@ namespace chip { -template -inline bool GetFlag(const FlagsT& inFlags, const FlagT inFlag) +template +inline bool GetFlag(const FlagsT & inFlags, const FlagT inFlag) { return (inFlags & static_cast(inFlag)) != 0; } -template -inline void ClearFlag(FlagsT& inFlags, const FlagT inFlag) +template +inline void ClearFlag(FlagsT & inFlags, const FlagT inFlag) { - inFlags &= ~ static_cast(inFlag); + inFlags &= ~static_cast(inFlag); } -template -inline void SetFlag(FlagsT& inFlags, const FlagT inFlag) +template +inline void SetFlag(FlagsT & inFlags, const FlagT inFlag) { inFlags |= static_cast(inFlag); } -template -inline void SetFlag(FlagsT& inFlags, const FlagT inFlag, const bool inValue) +template +inline void SetFlag(FlagsT & inFlags, const FlagT inFlag, const bool inValue) { if (inValue) { diff --git a/src/lib/support/Makefile.am b/src/lib/support/Makefile.am new file mode 100644 index 00000000000000..38c60449b15d30 --- /dev/null +++ b/src/lib/support/Makefile.am @@ -0,0 +1,44 @@ +# +# Copyright (c) 2020 Project CHIP Authors +# +# 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. +# + +# +# Description: +# This file is the GNU automake template for the CHIP support +# library. +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +SUBDIRS = \ + $(NULL) + +include SupportLayer.am + + +lib_LIBRARIES = libSupportLayer.a + +libSupportLayer_a_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + $(NULL) + +libSupportLayer_a_SOURCES = $(CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES) + +libSupportLayer_adir = $(includedir)/support + +dist_libSupportLayer_a_HEADERS = $(CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES) + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/support/SupportLayer.am b/src/lib/support/SupportLayer.am index ee81a2986296a0..2a422dcffecd91 100644 --- a/src/lib/support/SupportLayer.am +++ b/src/lib/support/SupportLayer.am @@ -23,24 +23,24 @@ # CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES = \ - support/Base64.cpp \ - support/CHIPFaultInjection.cpp \ - support/logging/CHIPLogging.cpp \ - support/ErrorStr.cpp \ - support/FibonacciUtils.cpp \ - support/RandUtils.cpp \ + @top_builddir@/src/lib/support/Base64.cpp \ + @top_builddir@/src/lib/support/CHIPFaultInjection.cpp \ + @top_builddir@/src/lib/support/ErrorStr.cpp \ + @top_builddir@/src/lib/support/FibonacciUtils.cpp \ + @top_builddir@/src/lib/support/logging/CHIPLogging.cpp \ + @top_builddir@/src/lib/support/RandUtils.cpp \ $(NULL) CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES = \ - support/CHIPFaultInjection.h \ - support/CodeUtils.h \ - support/DLLUtil.h \ - support/ErrorStr.h \ - support/FibonacciUtils.h \ - support/FlagUtils.hpp \ - support/logging/CHIPLogging.h \ - support/Base64.h \ - support/RandUtils.h \ + @top_builddir@/src/lib/support/CHIPFaultInjection.h \ + @top_builddir@/src/lib/support/CodeUtils.h \ + @top_builddir@/src/lib/support/DLLUtil.h \ + @top_builddir@/src/lib/support/ErrorStr.h \ + @top_builddir@/src/lib/support/FibonacciUtils.h \ + @top_builddir@/src/lib/support/FlagUtils.hpp \ + @top_builddir@/src/lib/support/logging/CHIPLogging.h \ + @top_builddir@/src/lib/support/Base64.h \ + @top_builddir@/src/lib/support/RandUtils.h \ $(NULL) if CHIP_WITH_NLFAULTINJECTION diff --git a/src/lib/support/logging/CHIPLogging.h b/src/lib/support/logging/CHIPLogging.h index 9721be94d34c81..5973a8252aa854 100644 --- a/src/lib/support/logging/CHIPLogging.h +++ b/src/lib/support/logging/CHIPLogging.h @@ -76,7 +76,7 @@ namespace Logging { */ enum LogModule { - kLogModule_NotSpecified = 0, + kLogModule_NotSpecified = 0, kLogModule_Inet, kLogModule_Ble, @@ -124,7 +124,7 @@ enum LogCategory * that no messages should be logged. * */ - kLogCategory_None = 0, + kLogCategory_None = 0, /*!< * Indicates a category of log message that describes an unexpected @@ -142,7 +142,7 @@ enum LogCategory * messages. * */ - kLogCategory_Error = 1, + kLogCategory_Error = 1, /*!< * Indicates a category of log message that describes an event @@ -160,7 +160,7 @@ enum LogCategory * requested resource types, error numbers, etc.). * */ - kLogCategory_Progress = 2, + kLogCategory_Progress = 2, /*!< * Indicates a category of log message that describes detailed @@ -171,19 +171,19 @@ enum LogCategory * kLogCategory_Progress categories. * */ - kLogCategory_Detail = 3, + kLogCategory_Detail = 3, /*!< * Indicates a category of log message that describes information * needed by IE and QA teams for automated testing. * */ - kLogCategory_Retain = 4, + kLogCategory_Retain = 4, - kLogCategory_Max = kLogCategory_Retain + kLogCategory_Max = kLogCategory_Retain }; -extern void Log(uint8_t module, uint8_t category, const char *msg, ...); +extern void Log(uint8_t module, uint8_t category, const char * msg, ...); extern uint8_t GetLogFilter(void); extern void SetLogFilter(uint8_t category); @@ -205,7 +205,8 @@ extern void SetLogFilter(uint8_t category); * */ #ifndef ChipLogError -#define ChipLogError(MOD, MSG, ...) chip::Logging::Log( chip::Logging::kLogModule_##MOD , chip::Logging::kLogCategory_Error, MSG, ## __VA_ARGS__) +#define ChipLogError(MOD, MSG, ...) \ + chip::Logging::Log(chip::Logging::kLogModule_##MOD, chip::Logging::kLogCategory_Error, MSG, ##__VA_ARGS__) #endif #else #define ChipLogError(MOD, MSG, ...) @@ -225,13 +226,13 @@ extern void SetLogFilter(uint8_t category); * */ #ifndef ChipLogProgress -#define ChipLogProgress(MOD, MSG, ...) chip::Logging::Log( chip::Logging::kLogModule_##MOD , chip::Logging::kLogCategory_Progress, MSG, ## __VA_ARGS__) +#define ChipLogProgress(MOD, MSG, ...) \ + chip::Logging::Log(chip::Logging::kLogModule_##MOD, chip::Logging::kLogCategory_Progress, MSG, ##__VA_ARGS__) #endif #else #define ChipLogProgress(MOD, MSG, ...) #endif - #ifndef CHIP_DETAIL_LOGGING #define CHIP_DETAIL_LOGGING 1 #endif @@ -246,7 +247,8 @@ extern void SetLogFilter(uint8_t category); * */ #ifndef ChipLogDetail -#define ChipLogDetail(MOD, MSG, ...) chip::Logging::Log( chip::Logging::kLogModule_##MOD , chip::Logging::kLogCategory_Detail, MSG, ## __VA_ARGS__) +#define ChipLogDetail(MOD, MSG, ...) \ + chip::Logging::Log(chip::Logging::kLogModule_##MOD, chip::Logging::kLogCategory_Detail, MSG, ##__VA_ARGS__) #endif #else #define ChipLogDetail(MOD, MSG, ...) @@ -254,7 +256,7 @@ extern void SetLogFilter(uint8_t category); #ifndef CHIP_RETAIN_LOGGING #define CHIP_RETAIN_LOGGING CHIP_PROGRESS_LOGGING -#define ChipLogRetain(MOD, MSG, ...) ChipLogProgress(MOD, MSG, ## __VA_ARGS__) +#define ChipLogRetain(MOD, MSG, ...) ChipLogProgress(MOD, MSG, ##__VA_ARGS__) #endif #if CHIP_RETAIN_LOGGING @@ -268,7 +270,8 @@ extern void SetLogFilter(uint8_t category); * */ #ifndef ChipLogRetain -#define ChipLogRetain(MOD, MSG, ...) chip::Logging::Log( chip::Logging::kLogModule_##MOD , chip::Logging::kLogCategory_Retain, MSG, ## __VA_ARGS__) +#define ChipLogRetain(MOD, MSG, ...) \ + chip::Logging::Log(chip::Logging::kLogModule_##MOD, chip::Logging::kLogCategory_Retain, MSG, ##__VA_ARGS__) #endif #else // #if CHIP_RETAIN_LOGGING @@ -280,7 +283,6 @@ extern void SetLogFilter(uint8_t category); #define ChipLogRetain(MOD, MSG, ...) #endif // #if CHIP_RETAIN_LOGGING - #if CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_RETAIN_LOGGING #define _CHIP_USE_LOGGING 1 #else @@ -293,23 +295,21 @@ extern void SetLogFilter(uint8_t category); #define ChipLoggingModuleNameLen 3 #define ChipLoggingMessageSeparatorLen 2 #define ChipLoggingMessageTrailerLen 2 -#define ChipLoggingTotalMessagePadding (ChipLoggingchipPrefixLen + \ - ChipLoggingModuleNameLen + \ - ChipLoggingMessageSeparatorLen + \ - ChipLoggingMessageTrailerLen) +#define ChipLoggingTotalMessagePadding \ + (ChipLoggingchipPrefixLen + ChipLoggingModuleNameLen + ChipLoggingMessageSeparatorLen + ChipLoggingMessageTrailerLen) -extern void GetMessageWithPrefix(char *buf, uint8_t bufSize, uint8_t module, const char *msg); -extern void GetModuleName(char *buf, uint8_t module); +extern void GetMessageWithPrefix(char * buf, uint8_t bufSize, uint8_t module, const char * msg); +extern void GetModuleName(char * buf, uint8_t module); void PrintMessagePrefix(uint8_t module); #else -static inline void GetMessageWithPrefix(char *buf, uint8_t bufSize, uint8_t module, const char *msg) +static inline void GetMessageWithPrefix(char * buf, uint8_t bufSize, uint8_t module, const char * msg) { return; } -static inline void GetModuleName(char *buf, uint8_t module) +static inline void GetModuleName(char * buf, uint8_t module) { return; } @@ -328,8 +328,6 @@ extern uint8_t gLogFilter; #endif // CHIP_LOG_FILTERING - - /** * @def ChipLogIfFalse(aCondition) * @@ -373,24 +371,21 @@ extern uint8_t gLogFilter; #if CHIP_CONFIG_ENABLE_CONDITION_LOGGING && !defined(ChipLogIfFalse) -#define ChipLogIfFalse(aCondition) \ -do \ -{ \ - if (!(aCondition)) \ - { \ - ChipLogError(NotSpecified, "Condition Failed (%s) at %s:%d", \ - #aCondition, __FILE__, __LINE__); \ - } \ -} while (0) +#define ChipLogIfFalse(aCondition) \ + do \ + { \ + if (!(aCondition)) \ + { \ + ChipLogError(NotSpecified, "Condition Failed (%s) at %s:%d", #aCondition, __FILE__, __LINE__); \ + } \ + } while (0) #else // CHIP_CONFIG_ENABLE_CONDITION_LOGGING -#define ChipLogIfFalse(aCondition) \ - IgnoreUnusedVariable(aCondition) +#define ChipLogIfFalse(aCondition) IgnoreUnusedVariable(aCondition) #endif // CHIP_CONFIG_ENABLE_CONDITION_LOGGING - /** * @def ChipLogFunctError(aErr) * @@ -433,24 +428,21 @@ do #if CHIP_CONFIG_ENABLE_FUNCT_ERROR_LOGGING && !defined(ChipLogFunctError) -#define ChipLogFunctError(aErr) \ -do \ -{ \ - if ((aErr) != CHIP_NO_ERROR) \ - { \ - ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(aErr), __FILE__, __LINE__);\ - } \ -} while (0) +#define ChipLogFunctError(aErr) \ + do \ + { \ + if ((aErr) != CHIP_NO_ERROR) \ + { \ + ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(aErr), __FILE__, __LINE__); \ + } \ + } while (0) #else // CHIP_CONFIG_ENABLE_FUNCT_ERROR_LOGGING -#define ChipLogFunctError(aErr) \ - IgnoreUnusedVariable(aErr) +#define ChipLogFunctError(aErr) IgnoreUnusedVariable(aErr) #endif // CHIP_CONFIG_ENABLE_FUNCT_ERROR_LOGGING - - } // namespace Logging } // namespace chip diff --git a/src/setup_payload/Makefile.am b/src/setup_payload/Makefile.am index 24fa05ac5c6c6b..61b1cd56eb9bfb 100644 --- a/src/setup_payload/Makefile.am +++ b/src/setup_payload/Makefile.am @@ -30,6 +30,7 @@ lib_LIBRARIES = libQrCode.a libQrCode_adir = $(includedir)/setup_payload libQrCode_a_CPPFLAGS = \ + -I$(top_srcdir)/src \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/system \ -I$(top_srcdir)/src/include/ \ @@ -42,11 +43,11 @@ libQrCode_a_SOURCES = \ QRCodeSetupPayloadParser.cpp \ $(NULL) -dist_libQrCode_a_HEADERS = \ - QRCodeSetupPayloadGenerator.h \ - SetupPayload.h \ - Base45.h \ - QRCodeSetupPayloadParser.h \ +dist_libQrCode_a_HEADERS = \ + QRCodeSetupPayloadGenerator.h \ + SetupPayload.h \ + Base45.h \ + QRCodeSetupPayloadParser.h \ $(NULL) $(RECURSIVE_TARGETS): $(lib_LIBRARIES) diff --git a/src/setup_payload/tests/Makefile.am b/src/setup_payload/tests/Makefile.am index 78bcce1b350db5..45e290a138c745 100644 --- a/src/setup_payload/tests/Makefile.am +++ b/src/setup_payload/tests/Makefile.am @@ -40,6 +40,7 @@ if CHIP_BUILD_TESTS # objects in this makefile. AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/system \ -I$(top_srcdir)/src/setup_payload \ diff --git a/src/system/Makefile.am b/src/system/Makefile.am index 20722af8b6c4ff..ebc0df28002e83 100644 --- a/src/system/Makefile.am +++ b/src/system/Makefile.am @@ -16,31 +16,35 @@ # # -# Description: -# This file is the GNU automake template for the CHIP system layer. +# Description: +# This file is the GNU automake template for the CHIP system layer +# library. +# include $(abs_top_nlbuild_autotools_dir)/automake/pre.am include SystemLayer.am -SUBDIRS = tests - -EXTRA_DIST = \ - SystemLayerPrivate.h \ +SUBDIRS = \ + tests \ $(NULL) -lib_LIBRARIES = libSystemLayer.a -libSystemLayer_a_CPPFLAGS = \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/include \ - -I$(top_srcdir)/src/system \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib/core \ - $(LWIP_CPPFLAGS) \ - $(SOCKETS_CPPFLAGS) \ +EXTRA_DIST = \ + SystemLayerPrivate.h \ $(NULL) +lib_LIBRARIES = libSystemLayer.a + +libSystemLayer_a_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/system \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib/core \ + $(LWIP_CPPFLAGS) \ + $(SOCKETS_CPPFLAGS) \ + $(NULL) libSystemLayer_a_SOURCES = $(CHIP_BUILD_SYSTEM_LAYER_SOURCE_FILES) @@ -48,4 +52,6 @@ libSystemLayer_adir = $(includedir)/system dist_libSystemLayer_a_HEADERS = $(CHIP_BUILD_SYSTEM_LAYER_HEADER_FILES) +$(RECURSIVE_TARGETS): $(lib_LIBRARIES) + include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/system/tests/Makefile.am b/src/system/tests/Makefile.am index 0c755020361997..500e3e6a76f549 100644 --- a/src/system/tests/Makefile.am +++ b/src/system/tests/Makefile.am @@ -1,7 +1,7 @@ # # Copyright (c) 2020 Project CHIP Authors -# Copyright (c) 2014-2018 Nest Labs, Inc. # Copyright (c) 2018 Google LLC +# Copyright (c) 2014-2018 Nest Labs, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,62 +28,85 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am # Local headers to build against and distribute but not to install # since they are not part of the package. # -noinst_HEADERS = \ +noinst_HEADERS = \ $(NULL) # # Other files we do want to distribute with the package. # -EXTRA_DIST = \ +EXTRA_DIST = \ $(NULL) if CHIP_BUILD_TESTS # C/C++ preprocessor option flags that will apply to all compiled # objects in this makefile. -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib/core \ - -I$(top_srcdir)/src/system \ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib/core \ + -I$(top_srcdir)/src/system \ $(NULL) -COMMON_LDADD = \ +CHIP_LDADD = \ + $(top_builddir)/src/system/libSystemLayer.a \ + $(top_builddir)/src/lib/support/libSupportLayer.a \ + $(NULL) + +COMMON_LDADD = \ + $(COMMON_LDFLAGS) \ + $(CHIP_LDADD) \ + $(LWIP_LDFLAGS) $(LWIP_LIBS) \ + $(SOCKETS_LDFLAGS) $(SOCKETS_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ $(NULL) # Test applications that should be run when the 'check' target is run. -check_PROGRAMS = \ - TestSystemLayer \ +check_PROGRAMS = \ + TestSystemLayer \ + TestSystemObject \ + TestSystemPacketBuffer \ + TestSystemTimer \ $(NULL) # Test applications and scripts that should be built and run when the # 'check' target is run. -TESTS = \ - $(check_PROGRAMS) \ +TESTS = \ + $(check_PROGRAMS) \ $(NULL) # The additional environment variables and their values that will be # made available to all programs and scripts in TESTS. -TESTS_ENVIRONMENT = \ +TESTS_ENVIRONMENT = \ $(NULL) # Source, compiler, and linker options for test programs. -TestSystemLayer_LDADD = $(COMMON_LDADD) -TestSystemLayer_SOURCES = tests.cpp +TestSystemLayer_SOURCES = tests.cpp +TestSystemLayer_LDADD = $(COMMON_LDADD) + +TestSystemObject_SOURCES = TestSystemObject.cpp +TestSystemObject_LDADD = $(COMMON_LDADD) + +TestSystemPacketBuffer_SOURCES = TestSystemPacketBuffer.cpp +TestSystemPacketBuffer_LDADD = $(COMMON_LDADD) + +TestSystemTimer_SOURCES = TestSystemTimer.cpp +TestSystemTimer_LDADD = $(COMMON_LDADD) if CHIP_BUILD_COVERAGE -CLEANFILES = $(wildcard *.gcda *.gcno) +CLEANFILES = $(wildcard *.gcda *.gcno) if CHIP_BUILD_COVERAGE_REPORTS # The bundle should positively be qualified with the absolute build # path. Otherwise, VPATH will get auto-prefixed to it if there is # already such a directory in the non-colocated source tree. -CHIP_COVERAGE_BUNDLE = ${abs_builddir}/${PACKAGE}${NL_COVERAGE_BUNDLE_SUFFIX} -CHIP_COVERAGE_INFO = ${CHIP_COVERAGE_BUNDLE}/${PACKAGE}${NL_COVERAGE_INFO_SUFFIX} +CHIP_COVERAGE_BUNDLE = ${abs_builddir}/${PACKAGE}${NL_COVERAGE_BUNDLE_SUFFIX} +CHIP_COVERAGE_INFO = ${CHIP_COVERAGE_BUNDLE}/${PACKAGE}${NL_COVERAGE_INFO_SUFFIX} $(CHIP_COVERAGE_BUNDLE): $(call create-directory) @@ -103,5 +126,3 @@ endif # CHIP_BUILD_COVERAGE endif # CHIP_BUILD_TESTS include $(abs_top_nlbuild_autotools_dir)/automake/post.am - - diff --git a/src/system/tests/TestSystemObject.cpp b/src/system/tests/TestSystemObject.cpp new file mode 100644 index 00000000000000..950de928cc5065 --- /dev/null +++ b/src/system/tests/TestSystemObject.cpp @@ -0,0 +1,537 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * 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. + */ + +/** + * @file + * This is a unit test suite for chip::System::Object, * + * the part of the CHIP System Layer that implements objects and + * their static allocation pools. + * + */ + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +// Install a sleep in the high water mark function, to force +// collisions between the threads that call it. + +// clang-format off +#define SYSTEM_OBJECT_HWM_TEST_HOOK() do { usleep(1000); } while(0) +// clang-format on + +#include + +#include + +#include + +#if CHIP_SYSTEM_CONFIG_USE_LWIP +#include +#include +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING +#include +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + +#include +#include +#include +#include + +// Test context +using namespace chip::System; + +namespace chip { +namespace System { + +using chip::ErrorStr; + +static int Initialize(void * aContext); +static int Finalize(void * aContext); + +class TestObject : public Object +{ +public: + Error Init(void); + + static void CheckRetention(nlTestSuite * inSuite, void * aContext); + static void CheckConcurrency(nlTestSuite * inSuite, void * aContext); + static void CheckHighWatermark(nlTestSuite * inSuite, void * aContext); + static void CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext); + +private: + enum + { + kPoolSize = 122 // a multiple of kNumThreads, less than CHIP_SYS_STATS_COUNT_MAX + }; + static ObjectPool sPool; + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + unsigned int mDelay; + + enum + { + kNumThreads = 16, + kLoopIterations = 100000, + kMaxDelayIterations = 3 + }; + + void Delay(volatile unsigned int & aAccumulator); + static void * CheckConcurrencyThread(void * aContext); + static void * CheckHighWatermarkThread(void * aContext); + static void MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) ); +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + // Not defined + TestObject(const TestObject &); + TestObject & operator=(const TestObject &); +}; + +ObjectPool TestObject::sPool; + +Error TestObject::Init(void) +{ +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + this->mDelay = kMaxDelayIterations > 0 ? 1 : 0; + if (kMaxDelayIterations > 1) + { + this->mDelay += static_cast(rand() % kMaxDelayIterations); + } +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + return CHIP_SYSTEM_NO_ERROR; +} + +struct TestContext +{ + nlTestSuite * mTestSuite; + void * mLayerContext; + volatile unsigned int mAccumulator; +}; + +struct TestContext sContext; + +// Test Object retention + +void TestObject::CheckRetention(nlTestSuite * inSuite, void * aContext) +{ + TestContext & lContext = *static_cast(aContext); + Layer lLayer; + unsigned int i, j; + + lLayer.Init(lContext.mLayerContext); + memset(&sPool, 0, sizeof(sPool)); + + for (i = 0; i < kPoolSize; ++i) + { + TestObject * lCreated = sPool.TryCreate(lLayer); + + NL_TEST_ASSERT(lContext.mTestSuite, lCreated != NULL); + if (lCreated == NULL) + continue; + NL_TEST_ASSERT(lContext.mTestSuite, lCreated->IsRetained(lLayer)); + NL_TEST_ASSERT(lContext.mTestSuite, &(lCreated->SystemLayer()) == &lLayer); + + lCreated->Init(); + + for (j = 0; j < kPoolSize; ++j) + { + TestObject * lGotten = sPool.Get(lLayer, j); + + if (j > i) + { + NL_TEST_ASSERT(lContext.mTestSuite, lGotten == NULL); + } + else + { + NL_TEST_ASSERT(lContext.mTestSuite, lGotten != NULL); + lGotten->Retain(); + } + } + } + + for (i = 0; i < kPoolSize; ++i) + { + TestObject * lGotten = sPool.Get(lLayer, i); + + NL_TEST_ASSERT(lContext.mTestSuite, lGotten != NULL); + + for (j = kPoolSize; j > i; --j) + { + NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer)); + lGotten->Release(); + } + + NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer)); + lGotten->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lGotten->IsRetained(lLayer)); + } + + for (i = 0; i < kPoolSize; ++i) + { + TestObject * lGotten = sPool.Get(lLayer, i); + + NL_TEST_ASSERT(lContext.mTestSuite, lGotten == NULL); + } + + lLayer.Shutdown(); +} + +// Test Object concurrency + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING +void TestObject::Delay(volatile unsigned int & aAccumulator) +{ + unsigned int lSum = 0; + + if (kMaxDelayIterations > 0) + { + for (unsigned int z = 0; z < this->mDelay; ++z) + { + lSum += rand(); + } + + lSum = lSum / this->mDelay; + } + + aAccumulator = lSum; +} + +void * TestObject::CheckConcurrencyThread(void * aContext) +{ + const unsigned int kNumObjects = kPoolSize / kNumThreads; + TestObject * lObject = NULL; + TestContext & lContext = *static_cast(aContext); + Layer lLayer; + unsigned int i; + + lLayer.Init(lContext.mLayerContext); + + // Take this thread's share of objects + + for (i = 0; i < kNumObjects; ++i) + { + while (lObject == NULL) + { + lObject = sPool.TryCreate(lLayer); + } + + NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer)); + NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer); + + lObject->Init(); + lObject->Delay(lContext.mAccumulator); + } + + // Free the last object of the pool, if it belongs to + // this thread. + + lObject = sPool.Get(lLayer, kPoolSize - 1); + + if (lObject != NULL) + { + lObject->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer)); + } + + // For each iteration, take one more object, and free one starting from the end + // of the pool + + for (i = 0; i < kLoopIterations; ++i) + { + unsigned int j; + + lObject = NULL; + while (lObject == NULL) + { + lObject = sPool.TryCreate(lLayer); + } + + NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer)); + NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer); + + lObject->Init(); + lObject->Delay(lContext.mAccumulator); + + j = kPoolSize; + lObject = NULL; + while (j-- > 0) + { + lObject = sPool.Get(lLayer, j); + + if (lObject == NULL) + continue; + + lObject->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer)); + break; + } + + NL_TEST_ASSERT(lContext.mTestSuite, lObject != NULL); + } + + // Cleanup + + for (i = 0; i < kPoolSize; ++i) + { + lObject = sPool.Get(lLayer, i); + + if (lObject == NULL) + continue; + + lObject->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer)); + } + + lLayer.Shutdown(); + + return aContext; +} + +void * TestObject::CheckHighWatermarkThread(void * aContext) +{ + TestContext & lContext = *static_cast(aContext); + unsigned int i; + chip::System::Stats::count_t lNumInUse; + chip::System::Stats::count_t lHighWatermark; + + i = (rand() % CHIP_SYS_STATS_COUNT_MAX); + + sPool.UpdateHighWatermark(i); + + sPool.GetStatistics(lNumInUse, lHighWatermark); + + NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark >= i); + if (lHighWatermark < i) + { + printf("hwm: %d, i: %u\n", lHighWatermark, i); + } + + return aContext; +} + +void TestObject::MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) ) +{ + TestContext & lContext = *static_cast(aContext); + pthread_t lThread[kNumThreads]; + + memset(&sPool, 0, sizeof(sPool)); + + for (unsigned int i = 0; i < kNumThreads; ++i) + { + int lError = pthread_create(&lThread[i], NULL, aStartRoutine, &lContext); + + NL_TEST_ASSERT(lContext.mTestSuite, lError == 0); + } + + for (unsigned int i = 0; i < kNumThreads; ++i) + { + int lError = pthread_join(lThread[i], NULL); + + NL_TEST_ASSERT(lContext.mTestSuite, lError == 0); + } +} +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + +void TestObject::CheckConcurrency(nlTestSuite * inSuite, void * aContext) +{ +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + MultithreadedTest(inSuite, aContext, CheckConcurrencyThread); +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING +} + +void TestObject::CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext) +{ +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + for (unsigned int i = 0; i < 1000; i++) + { + MultithreadedTest(inSuite, aContext, CheckHighWatermarkThread); + } +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING +} + +void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext) +{ + memset(&sPool, 0, sizeof(sPool)); + + const unsigned int kNumObjects = kPoolSize; + TestObject * lObject = NULL; + TestContext & lContext = *static_cast(aContext); + Layer lLayer; + unsigned int i; + chip::System::Stats::count_t lNumInUse; + chip::System::Stats::count_t lHighWatermark; + + lLayer.Init(lContext.mLayerContext); + + // Take all objects one at a time and check the watermark + // increases monotonically + + for (i = 0; i < kNumObjects; ++i) + { + lObject = sPool.TryCreate(lLayer); + + NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer)); + NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer); + + sPool.GetStatistics(lNumInUse, lHighWatermark); + NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1)); + NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == lNumInUse); + + lObject->Init(); + } + + // Fail an allocation and check that both stats don't change + + lObject = sPool.TryCreate(lLayer); + NL_TEST_ASSERT(lContext.mTestSuite, lObject == NULL); + + sPool.GetStatistics(lNumInUse, lHighWatermark); + NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == kNumObjects); + NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects); + + // Free all objects one at a time and check that the watermark does not + // change. + + for (i = 0; i < kNumObjects; ++i) + { + lObject = sPool.Get(lLayer, i); + + NL_TEST_ASSERT(lContext.mTestSuite, lObject != NULL); + + lObject->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer)); + + sPool.GetStatistics(lNumInUse, lHighWatermark); + NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (kNumObjects - i - 1)); + NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects); + } + + // Take all objects one at a time again and check the watermark + // does not move + + for (i = 0; i < kNumObjects; ++i) + { + lObject = sPool.TryCreate(lLayer); + + NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer)); + NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer); + + sPool.GetStatistics(lNumInUse, lHighWatermark); + NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1)); + NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects); + + lObject->Init(); + } + + // Cleanup + + for (i = 0; i < kPoolSize; ++i) + { + lObject = sPool.Get(lLayer, i); + + if (lObject == NULL) + continue; + + lObject->Release(); + NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer)); + } + + lLayer.Shutdown(); +} + +// Test Suite + +/** + * Test Suite. It lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("Retention", TestObject::CheckRetention), + NL_TEST_DEF("Concurrency", TestObject::CheckConcurrency), + NL_TEST_DEF("HighWatermark", TestObject::CheckHighWatermark), + NL_TEST_DEF("HighWatermarkConcurrency", TestObject::CheckHighWatermarkConcurrency), + NL_TEST_SENTINEL() +}; + +static nlTestSuite sTestSuite = +{ + "chip-system-object", + &sTests[0], + Initialize, + Finalize +}; +// clang-format on + +/** + * Initialize the test suite. + */ +static int Initialize(void * aContext) +{ + TestContext & lContext = *reinterpret_cast(aContext); + void * lLayerContext = NULL; + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + static sys_mbox * sLwIPEventQueue = NULL; + + if (sLwIPEventQueue == NULL) + { + sys_mbox_new(&sLwIPEventQueue, 100); + } + + lLayerContext = &sLwIPEventQueue; +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + lContext.mTestSuite = &sTestSuite; + lContext.mLayerContext = lLayerContext; + lContext.mAccumulator = 0; + + return SUCCESS; +} + +/** + * Finalize the test suite. + */ +static int Finalize(void * aContext) +{ + TestContext & lContext = *reinterpret_cast(aContext); + + lContext.mTestSuite = NULL; + + return SUCCESS; +} + +} // namespace System +} // namespace chip + +int main(int argc, char * argv[]) +{ + // Initialize standard pseudo-random number generator + srand(0); + + // Generate machine-readable, comma-separated value (CSV) output. + nl_test_set_output_style(OUTPUT_CSV); + + // Run test suit againt one lContext. + nlTestRunner(&sTestSuite, &chip::System::sContext); + + return nlTestRunnerStats(&sTestSuite); +} diff --git a/src/system/tests/TestSystemPacketBuffer.cpp b/src/system/tests/TestSystemPacketBuffer.cpp new file mode 100644 index 00000000000000..557fd08e51cea2 --- /dev/null +++ b/src/system/tests/TestSystemPacketBuffer.cpp @@ -0,0 +1,1204 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * 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. + */ + +/** + * @file + * This file implements a unit test suite for + * chip::System::PacketBuffer, a class that provides + * structure for network packet buffer management. + */ + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include +#include +#include +#include + +#include + +#if CHIP_SYSTEM_CONFIG_USE_LWIP +#include +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +#include + +using ::chip::System::PacketBuffer; + +#if !CHIP_SYSTEM_CONFIG_USE_LWIP +using ::chip::System::pbuf; +#endif + +// Test input vector format. + +struct TestContext +{ + uint16_t init_len; + uint16_t reserved_size; + uint8_t * start_buffer; + uint8_t * end_buffer; + uint8_t * payload_ptr; + struct pbuf * buf; +}; + +// Test input data. + +// clang-format off +static struct TestContext sContext[] = +{ + { 0, 0, NULL, NULL, NULL, NULL }, + { 0, 10, NULL, NULL, NULL, NULL }, + { 0, 128, NULL, NULL, NULL, NULL }, + { 0, 1536, NULL, NULL, NULL, NULL }, + { 0, CHIP_SYSTEM_PACKETBUFFER_SIZE, NULL, NULL, NULL, NULL } +}; +// clang-format on + +static const uint16_t sLengths[] = { 0, 1, 10, 128, CHIP_SYSTEM_PACKETBUFFER_SIZE, UINT16_MAX }; + +// Number of test context examples. +static const size_t kTestElements = sizeof(sContext) / sizeof(struct TestContext); +static const size_t kTestLengths = sizeof(sLengths) / sizeof(uint16_t); + +// Utility functions. + +#define TO_LWIP_PBUF(x) (reinterpret_cast(reinterpret_cast(x))) +#define OF_LWIP_PBUF(x) (reinterpret_cast(reinterpret_cast(x))) + +/** + * Free allocated test buffer memory. + */ +static void BufferFree(struct TestContext * theContext) +{ + if (theContext->buf != NULL) + { + PacketBuffer::Free(OF_LWIP_PBUF(theContext->buf)); + theContext->buf = NULL; + } +} + +/** + * Allocate memory for a test buffer and configure according to test context. + */ +static void BufferAlloc(struct TestContext * theContext) +{ + const size_t lInitialSize = CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size; + const size_t lAllocSize = CHIP_SYSTEM_PACKETBUFFER_SIZE; + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + u8_t lType, lFlags; +#if LWIP_PBUF_FROM_CUSTOM_POOLS + u16_t lPool; +#endif // LWIP_PBUF_FROM_CUSTOM_POOLS +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + if (theContext->buf == NULL) + { + theContext->buf = TO_LWIP_PBUF(PacketBuffer::New(0)); + } + + if (theContext->buf == NULL) + { + fprintf(stderr, "Failed to allocate %zuB memory: %s\n", lAllocSize, strerror(errno)); + exit(EXIT_FAILURE); + } + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + lType = theContext->buf->type; + lFlags = theContext->buf->flags; +#if LWIP_PBUF_FROM_CUSTOM_POOLS + lPool = theContext->buf->pool; +#endif // LWIP_PBUF_FROM_CUSTOM_POOLS + memset(theContext->buf, 0, lAllocSize); + theContext->buf->type = lType; + theContext->buf->flags = lFlags; +#if LWIP_PBUF_FROM_CUSTOM_POOLS + theContext->buf->pool = lPool; +#endif // LWIP_PBUF_FROM_CUSTOM_POOLS +#else // !CHIP_SYSTEM_CONFIG_USE_LWIP + memset(theContext->buf, 0, lAllocSize); +#if CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0 + theContext->buf->alloc_size = lAllocSize; +#endif // CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0 +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + theContext->start_buffer = reinterpret_cast(theContext->buf); + theContext->end_buffer = reinterpret_cast(theContext->buf) + lAllocSize; + + if (lInitialSize > lAllocSize) + { + theContext->payload_ptr = theContext->end_buffer; + } + else + { + theContext->payload_ptr = theContext->start_buffer + lInitialSize; + } +} + +/** + * Setup buffer layout as it is used by PacketBuffer class. + */ +static PacketBuffer * PrepareTestBuffer(struct TestContext * theContext) +{ + BufferAlloc(theContext); + + theContext->buf->next = NULL; + theContext->buf->payload = theContext->payload_ptr; + theContext->buf->ref = 1; + theContext->buf->len = theContext->init_len; + theContext->buf->tot_len = theContext->init_len; + + return reinterpret_cast(theContext->buf); +} +// Test functions invoked from the suite. + +/** + * Test PacketBuffer::Start() function. + */ +static void CheckStart(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + NL_TEST_ASSERT(inSuite, buffer->Start() == theContext->payload_ptr); + + theContext++; + } +} + +/** + * Test PacketBuffer::SetStart() function. + * + * Description: For every buffer-configuration from inContext, create a + * buffer's instance according to the configuration. Next, + * for any offset value from start_offset[], pass it to the + * buffer's instance through SetStart method. Then, verify that + * the beginning of the buffer has been correctly internally + * adjusted according to the offset value passed into the + * SetStart() method. + */ +static void CheckSetStart(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + static const ptrdiff_t sSizePacketBuffer = CHIP_SYSTEM_PACKETBUFFER_SIZE; + + for (size_t ith = 0; ith < kTestElements; ith++) + { + // clang-format off + static const ptrdiff_t start_offset[] = + { + -sSizePacketBuffer, + -128, + -1, + 0, + 1, + 128, + sSizePacketBuffer + }; + // clang-format on + + for (size_t s = 0; s < sizeof(start_offset) / sizeof(start_offset[0]); s++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + uint8_t * test_start = theContext->payload_ptr + start_offset[s]; + uint8_t * verify_start = test_start; + + buffer->SetStart(test_start); + + if (verify_start < theContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE) + { + // Set start before valid payload beginning. + verify_start = theContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE; + } + + if (verify_start > theContext->end_buffer) + { + // Set start after valid payload beginning. + verify_start = theContext->end_buffer; + } + + NL_TEST_ASSERT(inSuite, theContext->buf->payload == verify_start); + + if ((verify_start - theContext->payload_ptr) > theContext->init_len) + { + // Set start to the beginning of payload, right after buffer's header. + NL_TEST_ASSERT(inSuite, theContext->buf->len == 0); + } + else + { + // Set start to somewhere between the end of the buffer's + // header and the end of payload. + NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->init_len - (verify_start - theContext->payload_ptr))); + } + } + theContext++; + } +} + +/** + * Test PacketBuffer::DataLength() function. + */ +static void CheckDataLength(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + NL_TEST_ASSERT(inSuite, buffer->DataLength() == theContext->buf->len); + + theContext++; + } +} + +/** + * Test PacketBuffer::SetDataLength() function. + * + * Description: Take two initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. For any two buffers, call SetDataLength with + * different value from sLength[]. If two buffers are created with + * the same configuration, test SetDataLength on one buffer, + * without specifying the head of the buffer chain. Otherwise, + * test SetDataLength with one buffer being down the chain and the + * other one being passed as the head of the chain. After calling + * the method verify that data lenghts were correctly adjusted. + */ +static void CheckSetDataLength(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + for (size_t n = 0; n < kTestLengths; n++) + { + PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); + PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); + + if (theFirstContext == theSecondContext) + { + // headOfChain (the second arg) is NULL + buffer_2->SetDataLength(sLengths[n], NULL); + + if (sLengths[n] > (theSecondContext->end_buffer - theSecondContext->payload_ptr)) + { + NL_TEST_ASSERT( + inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr)); + NL_TEST_ASSERT(inSuite, + theSecondContext->buf->tot_len == + (theSecondContext->end_buffer - theSecondContext->payload_ptr)); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); + } + else + { + NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == sLengths[n]); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); + } + } + else + { + // headOfChain (the second arg) is buffer_1 + buffer_2->SetDataLength(sLengths[n], buffer_1); + + if (sLengths[n] > (theSecondContext->end_buffer - theSecondContext->payload_ptr)) + { + NL_TEST_ASSERT( + inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr)); + NL_TEST_ASSERT(inSuite, + theSecondContext->buf->tot_len == + (theSecondContext->end_buffer - theSecondContext->payload_ptr)); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); + + NL_TEST_ASSERT(inSuite, + theFirstContext->buf->tot_len == + (theFirstContext->init_len + + static_cast(theSecondContext->end_buffer - theSecondContext->payload_ptr) - + static_cast(theSecondContext->init_len))); + } + else + { + NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == sLengths[n]); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); + + NL_TEST_ASSERT(inSuite, + theFirstContext->buf->tot_len == + (theFirstContext->init_len + static_cast(sLengths[n]) - + static_cast(theSecondContext->init_len))); + } + } + } + + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::TotalLength() function. + */ +static void CheckTotalLength(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + NL_TEST_ASSERT(inSuite, buffer->TotalLength() == theContext->init_len); + + theContext++; + } +} + +/** + * Test PacketBuffer::MaxDataLength() function. + */ +static void CheckMaxDataLength(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + NL_TEST_ASSERT(inSuite, buffer->MaxDataLength() == (theContext->end_buffer - theContext->payload_ptr)); + + theContext++; + } +} + +/** + * Test PacketBuffer::AvailableDataLength() function. + */ +static void CheckAvailableDataLength(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + NL_TEST_ASSERT( + inSuite, buffer->AvailableDataLength() == ((theContext->end_buffer - theContext->payload_ptr) - theContext->init_len)); + + theContext++; + } +} + +/** + * Test PacketBuffer::ReservedSize() function. + */ +static void CheckReservedSize(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer & lBuffer = *PrepareTestBuffer(theContext); + const size_t kAllocSize = lBuffer.AllocSize(); + + if (theContext->reserved_size > kAllocSize) + { + NL_TEST_ASSERT(inSuite, lBuffer.ReservedSize() == kAllocSize); + } + else + { + NL_TEST_ASSERT(inSuite, lBuffer.ReservedSize() == theContext->reserved_size); + } + + theContext++; + } +} + +/** + * Test PacketBuffer::AddToEnd() function. + * + * Description: Take three initial configurations of PacketBuffer from + * inContext, create three PacketBuffers based on those + * configurations and then link those buffers together with + * PacketBuffer:AddToEnd(). Then, assert that after connecting + * buffers together, their internal states are correctly updated. + * This test function tests linking any combination of three + * buffer-configurations passed within inContext. + */ +static void CheckAddToEnd(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + struct TestContext * theThirdContext = static_cast(inContext); + + for (size_t kth = 0; kth < kTestElements; kth++) + { + PacketBuffer * buffer_1 = NULL; + PacketBuffer * buffer_2 = NULL; + PacketBuffer * buffer_3 = NULL; + + if (theFirstContext == theSecondContext || theFirstContext == theThirdContext || + theSecondContext == theThirdContext) + { + theThirdContext++; + continue; + } + + buffer_1 = PrepareTestBuffer(theFirstContext); + buffer_2 = PrepareTestBuffer(theSecondContext); + buffer_3 = PrepareTestBuffer(theThirdContext); + + buffer_1->AddToEnd(buffer_2); + + NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == (theFirstContext->init_len + theSecondContext->init_len)); + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); + + NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == NULL); + + buffer_1->AddToEnd(buffer_3); + + NL_TEST_ASSERT(inSuite, + theFirstContext->buf->tot_len == + (theFirstContext->init_len + theSecondContext->init_len + theThirdContext->init_len)); + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); + NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == theThirdContext->buf); + NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == NULL); + + theThirdContext++; + } + + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::DetachTail() function. + * + * Description: Take two initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. Next, link those buffers together, with the first + * buffer instance pointing to the second one. Then, call DetachTail() + * on the first buffer to unlink the second buffer. After the call, + * verify correct internal state of the first buffer. + */ +static void CheckDetachTail(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); + PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); + PacketBuffer * returned = NULL; + + if (theFirstContext != theSecondContext) + { + theFirstContext->buf->next = theSecondContext->buf; + theFirstContext->buf->tot_len += theSecondContext->init_len; + } + + returned = buffer_1->DetachTail(); + + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == NULL); + NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == theFirstContext->init_len); + + if (theFirstContext != theSecondContext) + { + NL_TEST_ASSERT(inSuite, returned == buffer_2); + } + + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::CompactHead() function. + * + * Description: Take two initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. Next, set both buffers' data length to any + * combination of values from sLengths[] and link those buffers + * into a chain. Then, call CompactHead() on the first buffer in + * the chain. After calling the method, verify correctly adjusted + * state of the first buffer. + */ +static void CheckCompactHead(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + // start with various initial length for the first buffer + for (size_t k = 0; k < kTestLengths; k++) + { + // start with various initial length for the second buffer + for (size_t l = 0; l < kTestLengths; l++) + { + PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); + PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); + uint16_t len1 = 0; + uint16_t len2 = 0; + + buffer_1->SetDataLength(sLengths[k], buffer_1); + len1 = buffer_1->DataLength(); + + if (theFirstContext != theSecondContext) + { + theFirstContext->buf->next = theSecondContext->buf; + + // Add various lengths to the second buffer + buffer_2->SetDataLength(sLengths[l], buffer_1); + len2 = buffer_2->DataLength(); + } + + buffer_1->CompactHead(); + + NL_TEST_ASSERT(inSuite, + theFirstContext->buf->payload == + (theFirstContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE)); + + /* verify length of the first buffer */ + if (theFirstContext == theSecondContext) + { + NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == len1); + } + else if (theFirstContext->buf->tot_len > buffer_1->MaxDataLength()) + { + NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == buffer_1->MaxDataLength()); + NL_TEST_ASSERT(inSuite, + theSecondContext->buf->len == theFirstContext->buf->tot_len - buffer_1->MaxDataLength()); + } + else + { + NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == theFirstContext->buf->tot_len); + if (len1 >= buffer_1->MaxDataLength() && len2 == 0) + { + /* make sure the second buffer is not freed */ + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); + } + else + { + /* make sure the second buffer is freed */ + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == NULL); + theSecondContext->buf = NULL; + } + } + } + } + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::ConsumeHead() function. + * + * Description: For every buffer-configuration from inContext, create a + * buffer's instance according to the configuration. Next, + * for any value from sLengths[], pass it to the buffer's + * instance through ConsumeHead() method. Then, verify that + * the internal state of the buffer has been correctly + * adjusted according to the value passed into the method. + */ +static void CheckConsumeHead(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + for (size_t n = 0; n < kTestLengths; n++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + + buffer->ConsumeHead(sLengths[n]); + + if (sLengths[n] > theContext->init_len) + { + NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + theContext->init_len)); + NL_TEST_ASSERT(inSuite, theContext->buf->len == 0); + NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == 0); + } + else + { + NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + sLengths[n])); + NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->buf->len - sLengths[n])); + NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == (theContext->buf->tot_len - sLengths[n])); + } + + if (theContext->buf->ref == 0) + { + theContext->buf = NULL; + } + } + + theContext++; + } +} + +/** + * Test PacketBuffer::Consume() function. + * + * Description: Take two different initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. Next, set both buffers' data length to any + * combination of values from sLengths[] and link those buffers + * into a chain. Then, call Consume() on the first buffer in + * the chain with all values from sLengths[]. After calling the + * method, verify correctly adjusted the state of the first + * buffer and appropriate return pointer from the method's call. + */ +static void CheckConsume(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + // consume various amounts of memory + for (size_t c = 0; c < kTestLengths; c++) + { + // start with various initial length for the first buffer + for (size_t k = 0; k < kTestLengths; k++) + { + // start with various initial length for the second buffer + for (size_t l = 0; l < kTestLengths; l++) + { + PacketBuffer * buffer_1; + PacketBuffer * buffer_2; + PacketBuffer * returned; + uint16_t buf_1_len = 0; + uint16_t buf_2_len = 0; + + if (theFirstContext == theSecondContext) + { + continue; + } + + buffer_1 = PrepareTestBuffer(theFirstContext); + buffer_2 = PrepareTestBuffer(theSecondContext); + + theFirstContext->buf->next = theSecondContext->buf; + + // Add various lengths to buffers + buffer_1->SetDataLength(sLengths[k], buffer_1); + buffer_2->SetDataLength(sLengths[l], buffer_1); + + buf_1_len = theFirstContext->buf->len; + buf_2_len = theSecondContext->buf->len; + + returned = buffer_1->Consume(sLengths[c]); + + if (sLengths[c] == 0) + { + NL_TEST_ASSERT(inSuite, returned == buffer_1); + continue; + } + + if (sLengths[c] < buf_1_len) + { + NL_TEST_ASSERT(inSuite, returned == buffer_1); + } + else if ((sLengths[c] >= buf_1_len) && + (sLengths[c] < buf_1_len + buf_2_len || (sLengths[c] == buf_1_len + buf_2_len && buf_2_len == 0))) + { + NL_TEST_ASSERT(inSuite, returned == buffer_2); + theFirstContext->buf = NULL; + } + else if (sLengths[c] >= (buf_1_len + buf_2_len)) + { + NL_TEST_ASSERT(inSuite, returned == NULL); + theFirstContext->buf = NULL; + theSecondContext->buf = NULL; + } + } + } + } + + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::EnsureReservedSize() function. + * + * Description: For every buffer-configuration from inContext, create a + * buffer's instance according to the configuration. Next, + * manually specify how much space is reserved in the buffer. + * Then, verify that EnsureReservedSize() method correctly + * retrieves the amount of the reserved space. + */ +static void CheckEnsureReservedSize(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + for (size_t n = 0; n < kTestLengths; n++) + { + PacketBuffer & lBuffer = *PrepareTestBuffer(theContext); + const size_t kAllocSize = lBuffer.AllocSize(); + uint16_t reserved_size = theContext->reserved_size; + + if (CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size > kAllocSize) + { + reserved_size = kAllocSize - CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE; + } + + if (sLengths[n] <= reserved_size) + { + NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == true); + continue; + } + + if ((sLengths[n] + theContext->init_len) > (kAllocSize - CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE)) + { + NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == false); + continue; + } + + NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == true); + NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + sLengths[n] - reserved_size)); + } + + theContext++; + } +} + +/** + * Test PacketBuffer::AlignPayload() function. + * + * Description: For every buffer-configuration from inContext, create a + * buffer's instance according to the configuration. Next, + * manually specify how much space is reserved and the + * required payload shift. Then, verify that AlignPayload() + * method correctly aligns the payload start pointer. + */ +static void CheckAlignPayload(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + for (size_t n = 0; n < kTestLengths - 1; n++) + { + PacketBuffer & lBuffer = *PrepareTestBuffer(theContext); + const size_t kAllocSize = lBuffer.AllocSize(); + + if (sLengths[n] == 0) + { + NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == false); + continue; + } + + uint16_t reserved_size = theContext->reserved_size; + if (theContext->reserved_size > kAllocSize) + { + reserved_size = kAllocSize; + } + + uint16_t payload_offset = (unsigned long) lBuffer.Start() % sLengths[n]; + uint16_t payload_shift = 0; + if (payload_offset > 0) + payload_shift = sLengths[n] - payload_offset; + + if (payload_shift <= kAllocSize - reserved_size) + { + NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == true); + NL_TEST_ASSERT(inSuite, ((unsigned long) lBuffer.Start() % sLengths[n]) == 0); + } + else + { + NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == false); + } + } + + theContext++; + } +} + +/** + * Test PacketBuffer::Next() function. + */ +static void CheckNext(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); + PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); + + if (theFirstContext != theSecondContext) + { + theFirstContext->buf->next = theSecondContext->buf; + + NL_TEST_ASSERT(inSuite, buffer_1->Next() == buffer_2); + } + else + { + NL_TEST_ASSERT(inSuite, buffer_1->Next() == NULL); + } + + NL_TEST_ASSERT(inSuite, buffer_2->Next() == NULL); + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::AddRef() function. + */ +static void CheckAddRef(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + PacketBuffer * buffer = PrepareTestBuffer(theContext); + buffer->AddRef(); + + NL_TEST_ASSERT(inSuite, theContext->buf->ref == 2); + + theContext++; + } +} + +/** + * Test PacketBuffer::NewWithAvailableSize() and PacketBuffer::Free() functions. + * + * Description: For every buffer-configuration from inContext, create a + * buffer's instance using NewWithAvailableSize() method. Then, verify that + * when the size of the reserved space passed to NewWithAvailableSize() is + * greater than #CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX, the method + * returns NULL. Otherwise, check for correctness of initializing + * the new buffer's internal state. Finally, free the buffer. + */ +static void CheckNewWithAvailableSizeAndFree(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theContext = (struct TestContext *) (inContext); + PacketBuffer * buffer; + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct pbuf * pb = NULL; + + buffer = PacketBuffer::NewWithAvailableSize(theContext->reserved_size, 0); + + if (theContext->reserved_size > CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX) + { + NL_TEST_ASSERT(inSuite, buffer == NULL); + theContext++; + continue; + } + + NL_TEST_ASSERT(inSuite, theContext->reserved_size <= buffer->AllocSize()); + NL_TEST_ASSERT(inSuite, buffer != NULL); + + if (buffer != NULL) + { + pb = TO_LWIP_PBUF(buffer); + + NL_TEST_ASSERT(inSuite, pb->len == 0); + NL_TEST_ASSERT(inSuite, pb->tot_len == 0); + NL_TEST_ASSERT(inSuite, pb->next == NULL); + NL_TEST_ASSERT(inSuite, pb->ref == 1); + } + + PacketBuffer::Free(buffer); + + theContext++; + } + + // Use the rest of the buffer space + do + { + buffer = PacketBuffer::NewWithAvailableSize(0, 0); + } while (buffer != NULL); +} + +/** + * Test PacketBuffer::Free() function. + * + * Description: Take two different initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. Next, chain two buffers together and set each + * buffer's reference count to one of the values from + * init_ret_count[]. Then, call Free() on the first buffer in + * the chain and verify correctly adjusted states of the two + * buffers. + */ +static void CheckFree(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + const uint16_t init_ref_count[] = { 1, 2, 3 }; + const int refs = sizeof(init_ref_count) / sizeof(uint16_t); + + // start with various buffer ref counts + for (size_t r = 0; r < refs; r++) + { + PacketBuffer * buffer_1; + + if (theFirstContext == theSecondContext) + { + continue; + } + + buffer_1 = PrepareTestBuffer(theFirstContext); + (void) PrepareTestBuffer(theSecondContext); + + theFirstContext->buf->next = theSecondContext->buf; + + // Add various buffer ref counts + theFirstContext->buf->ref = init_ref_count[r]; + theSecondContext->buf->ref = init_ref_count[(r + 1) % refs]; + + PacketBuffer::Free(buffer_1); + + NL_TEST_ASSERT(inSuite, theFirstContext->buf->ref == (init_ref_count[r] - 1)); + + if (init_ref_count[r] == 1) + { + NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs] - 1)); + } + else + { + NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs])); + } + + if (init_ref_count[r] > 1) + { + NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); + } + + if (theFirstContext->buf->ref == 0) + { + theFirstContext->buf = NULL; + } + + if (theSecondContext->buf->ref == 0) + { + theSecondContext->buf = NULL; + } + } + + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::FreeHead() function. + * + * Description: Take two different initial configurations of PacketBuffer from + * inContext and create two PacketBuffer instances based on those + * configurations. Next, chain two buffers together. Then, call + * FreeHead() on the first buffer in the chain and verify that + * the method returned pointer to the second buffer. + */ +static void CheckFreeHead(nlTestSuite * inSuite, void * inContext) +{ + struct TestContext * theFirstContext = static_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + struct TestContext * theSecondContext = static_cast(inContext); + + for (size_t jth = 0; jth < kTestElements; jth++) + { + PacketBuffer * buffer_1; + PacketBuffer * buffer_2; + PacketBuffer * returned = NULL; + + if (theFirstContext == theSecondContext) + { + continue; + } + + buffer_1 = PrepareTestBuffer(theFirstContext); + buffer_2 = PrepareTestBuffer(theSecondContext); + + theFirstContext->buf->next = theSecondContext->buf; + + returned = PacketBuffer::FreeHead(buffer_1); + + NL_TEST_ASSERT(inSuite, returned == buffer_2); + + theFirstContext->buf = NULL; + theSecondContext++; + } + + theFirstContext++; + } +} + +/** + * Test PacketBuffer::BuildFreeList() function. + */ +static void CheckBuildFreeList(nlTestSuite * inSuite, void * inContext) +{ + // BuildFreeList() is a private method called automatically. + (void) inSuite; + (void) inContext; +} + +/** + * Test Suite. It lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("PacketBuffer::NewWithAvailableSize&PacketBuffer::Free", CheckNewWithAvailableSizeAndFree), + NL_TEST_DEF("PacketBuffer::Start", CheckStart), + NL_TEST_DEF("PacketBuffer::SetStart", CheckSetStart), + NL_TEST_DEF("PacketBuffer::DataLength", CheckDataLength), + NL_TEST_DEF("PacketBuffer::SetDataLength", CheckSetDataLength), + NL_TEST_DEF("PacketBuffer::TotalLength", CheckTotalLength), + NL_TEST_DEF("PacketBuffer::MaxDataLength", CheckMaxDataLength), + NL_TEST_DEF("PacketBuffer::AvailableDataLength", CheckAvailableDataLength), + NL_TEST_DEF("PacketBuffer::ReservedSize", CheckReservedSize), + NL_TEST_DEF("PacketBuffer::AddToEnd", CheckAddToEnd), + NL_TEST_DEF("PacketBuffer::DetachTail", CheckDetachTail), + NL_TEST_DEF("PacketBuffer::CompactHead", CheckCompactHead), + NL_TEST_DEF("PacketBuffer::ConsumeHead", CheckConsumeHead), + NL_TEST_DEF("PacketBuffer::Consume", CheckConsume), + NL_TEST_DEF("PacketBuffer::EnsureReservedSize", CheckEnsureReservedSize), + NL_TEST_DEF("PacketBuffer::AlignPayload", CheckAlignPayload), + NL_TEST_DEF("PacketBuffer::Next", CheckNext), + NL_TEST_DEF("PacketBuffer::AddRef", CheckAddRef), + NL_TEST_DEF("PacketBuffer::Free", CheckFree), + NL_TEST_DEF("PacketBuffer::FreeHead", CheckFreeHead), + NL_TEST_DEF("PacketBuffer::BuildFreeList", CheckBuildFreeList), + + NL_TEST_SENTINEL() +}; +// clang-format on + +/** + * Set up the test suite. + * + * This is a work-around to initiate PacketBuffer protected class instance's data and set it to a known state, before an instance + * is created. + */ +static int TestSetup(void * inContext) +{ + struct TestContext * theContext = reinterpret_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + BufferAlloc(theContext); + theContext++; + } + + return (SUCCESS); +} + +/** + * Tear down the test suite. + * + * Free memory reserved at TestSetup. + */ +static int TestTeardown(void * inContext) +{ + struct TestContext * theContext = reinterpret_cast(inContext); + + for (size_t ith = 0; ith < kTestElements; ith++) + { + BufferFree(theContext); + theContext++; + } + + return (SUCCESS); +} + +int main(void) +{ + // clang-format off + nlTestSuite theSuite = + { + "chip-system-packetbuffer", + &sTests[0], + TestSetup, + TestTeardown + }; + // clang-format on + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + tcpip_init(NULL, NULL); +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + // Generate machine-readable, comma-separated value (CSV) output. + nl_test_set_output_style(OUTPUT_CSV); + + // Run test suit againt one context. + nlTestRunner(&theSuite, &sContext); + + return nlTestRunnerStats(&theSuite); +} diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp new file mode 100644 index 00000000000000..b7f598ace9d451 --- /dev/null +++ b/src/system/tests/TestSystemTimer.cpp @@ -0,0 +1,266 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * 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. + */ + +/** + * @file + * This is a unit test suite for chip::System::Timer, + * the part of the CHIP System Layer that implements timers. + * + */ + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#include +#include +#include + +#include + +#if CHIP_SYSTEM_CONFIG_USE_LWIP +#include +#include +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#include +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + +#include +#include +#include + +#include + +#include + +using chip::ErrorStr; +using namespace chip::System; + +static void ServiceEvents(Layer & aLayer, ::timeval & aSleepTime) +{ +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + fd_set readFDs, writeFDs, exceptFDs; + int numFDs = 0; + + FD_ZERO(&readFDs); + FD_ZERO(&writeFDs); + FD_ZERO(&exceptFDs); + + if (aLayer.State() == kLayerState_Initialized) + aLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); + + int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime); + if (selectRes < 0) + { + printf("select failed: %s\n", ErrorStr(MapErrorPOSIX(errno))); + return; + } +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + + if (aLayer.State() == kLayerState_Initialized) + { +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + aLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + if (aLayer.State() == kLayerState_Initialized) + { + // TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce + // aSleepTime according to the next timer. + aLayer.HandlePlatformTimer(); + } +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + } +} + +// Test input vector format. + +struct TestContext +{ + Layer * mLayer; + nlTestSuite * mTestSuite; +}; + +// Test input data. + +static struct TestContext sContext; + +static volatile bool sOverflowTestDone; + +void HandleTimer0Failed(Layer * inetLayer, void * aState, Error aError) +{ + TestContext & lContext = *static_cast(aState); + NL_TEST_ASSERT(lContext.mTestSuite, false); + sOverflowTestDone = true; +} + +void HandleTimer1Failed(Layer * inetLayer, void * aState, Error aError) +{ + TestContext & lContext = *static_cast(aState); + NL_TEST_ASSERT(lContext.mTestSuite, false); + sOverflowTestDone = true; +} + +void HandleTimer10Success(Layer * inetLayer, void * aState, Error aError) +{ + TestContext & lContext = *static_cast(aState); + NL_TEST_ASSERT(lContext.mTestSuite, true); + sOverflowTestDone = true; +} + +static void CheckOverflow(nlTestSuite * inSuite, void * aContext) +{ + uint32_t timeout_overflow_0ms = 652835029; + uint32_t timeout_overflow_1ms = 1958505088; + uint32_t timeout_10ms = 10; + + TestContext & lContext = *static_cast(aContext); + Layer & lSys = *lContext.mLayer; + + sOverflowTestDone = false; + + lSys.StartTimer(timeout_overflow_0ms, HandleTimer0Failed, aContext); + lSys.StartTimer(timeout_overflow_1ms, HandleTimer1Failed, aContext); + lSys.StartTimer(timeout_10ms, HandleTimer10Success, aContext); + + while (!sOverflowTestDone) + { + struct timeval sleepTime; + sleepTime.tv_sec = 0; + sleepTime.tv_usec = 1000; // 1 ms tick + ServiceEvents(lSys, sleepTime); + } + + lSys.CancelTimer(HandleTimer0Failed, aContext); + lSys.CancelTimer(HandleTimer1Failed, aContext); + lSys.CancelTimer(HandleTimer10Success, aContext); +} + +static uint32_t sNumTimersHandled = 0; +static const uint32_t MAX_NUM_TIMERS = 1000; + +void HandleGreedyTimer(Layer * aLayer, void * aState, Error aError) +{ + TestContext & lContext = *static_cast(aState); + NL_TEST_ASSERT(lContext.mTestSuite, sNumTimersHandled < MAX_NUM_TIMERS); + + if (sNumTimersHandled >= MAX_NUM_TIMERS) + { + return; + } + + aLayer->StartTimer(0, HandleGreedyTimer, aState); + sNumTimersHandled++; +} + +static void CheckStarvation(nlTestSuite * inSuite, void * aContext) +{ + TestContext & lContext = *static_cast(aContext); + Layer & lSys = *lContext.mLayer; + struct timeval sleepTime; + + lSys.StartTimer(0, HandleGreedyTimer, aContext); + + sleepTime.tv_sec = 0; + sleepTime.tv_usec = 1000; // 1 ms tick + ServiceEvents(lSys, sleepTime); +} + +// Test Suite + +/** + * Test Suite. It lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("Timer::TestOverflow", CheckOverflow), + NL_TEST_DEF("Timer::TestTimerStarvation", CheckStarvation), + NL_TEST_SENTINEL() +}; +// clang-format on + +static int TestSetup(void * aContext); +static int TestTeardown(void * aContext); + +// clang-format off +static nlTestSuite kTheSuite = +{ + "chip-system-timer", + &sTests[0], + TestSetup, + TestTeardown +}; +// clang-format on + +/** + * Set up the test suite. + */ +static int TestSetup(void * aContext) +{ + static Layer sLayer; + + TestContext & lContext = *reinterpret_cast(aContext); + void * lLayerContext = NULL; + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + static sys_mbox * sLwIPEventQueue = NULL; + + sys_mbox_new(&sLwIPEventQueue, 100); + tcpip_init(NULL, NULL); + lLayerContext = &sLwIPEventQueue; +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + sLayer.Init(lLayerContext); + + lContext.mLayer = &sLayer; + lContext.mTestSuite = &kTheSuite; + + return (SUCCESS); +} + +/** + * Tear down the test suite. + * Free memory reserved at TestSetup. + */ +static int TestTeardown(void * aContext) +{ + TestContext & lContext = *reinterpret_cast(aContext); + + lContext.mLayer->Shutdown(); + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + tcpip_finish(NULL, NULL); +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + return (SUCCESS); +} + +int main(int argc, char * argv[]) +{ + // Generate machine-readable, comma-separated value (CSV) output. + nl_test_set_output_style(OUTPUT_CSV); + + // Run test suit againt one lContext. + nlTestRunner(&kTheSuite, &sContext); + + return nlTestRunnerStats(&kTheSuite); +}