Skip to content

Commit

Permalink
Add more unit tests for coverage (#175)
Browse files Browse the repository at this point in the history
* Add unit test to cover httpParserOnStatusCompleteCallback, HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG and HTTP_REQUEST_NO_USER_AGENT_FLAG
* Enforce 100 percent coverage in CI
  • Loading branch information
chinglee-iot authored May 7, 2024
1 parent dd62128 commit 51d580b
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
uses: FreeRTOS/CI-CD-Github-Actions/coverage-cop@main
with:
coverage-file: ./build/coverage.info
branch-coverage-min: 100
line-coverage-min: 100

complexity:
runs-on: ubuntu-latest
Expand Down
153 changes: 139 additions & 14 deletions test/unit-test/core_http_send_utest.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@

/* HTTP OK Status-Line. */
#define HTTP_STATUS_LINE_OK "HTTP/1.1 200 OK\r\n"
#define HTTP_STATUS_LINE_NO_REASON_PHRASE "HTTP/1.1 200\r\n"
#define HTTP_STATUS_CODE_OK 200

/* Various header lines for test response templates. */
Expand Down Expand Up @@ -103,11 +104,26 @@
HTTP_TEST_VARY_HEADER_LINE \
HTTP_TEST_P3P_HEADER_LINE \
HTTP_TEST_XSERVER_HEADER_LINE HTTP_HEADER_LINE_SEPARATOR
#define HTTP_TEST_RESPONSE_HEAD_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD ) - 1U )
#define HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT 7
#define HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH 43
#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U )
#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U )
#define HTTP_TEST_RESPONSE_HEAD_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD ) - 1U )
#define HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT 7
#define HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH 43
#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U )
#define HTTP_TEST_RESPONSE_HEAD_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_OK ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U )

#define HTTP_TEST_RESPONSE_HEAD_2 \
HTTP_STATUS_LINE_NO_REASON_PHRASE \
HTTP_TEST_CONTENT_LENGTH_HEADER_LINE \
HTTP_TEST_CONNECTION_CLOSE_HEADER_LINE \
HTTP_TEST_DATE_HEADER_LINE \
HTTP_TEST_ETAG_HEADER_LINE \
HTTP_TEST_VARY_HEADER_LINE \
HTTP_TEST_P3P_HEADER_LINE \
HTTP_TEST_XSERVER_HEADER_LINE HTTP_HEADER_LINE_SEPARATOR
#define HTTP_TEST_RESPONSE_HEAD_2_LENGTH ( sizeof( HTTP_TEST_RESPONSE_HEAD_2 ) - 1U )
#define HTTP_TEST_RESPONSE_HEAD_2_HEADER_COUNT 7
#define HTTP_TEST_RESPONSE_HEAD_2_CONTENT_LENGTH 43
#define HTTP_TEST_RESPONSE_HEAD_2_PARTIAL_HEADER_FIELD_LENGTH ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_FIELD ) - 2U )
#define HTTP_TEST_RESPONSE_HEAD_2_PARTIAL_HEADER_VALUE_LENGTH ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) + sizeof( HTTP_TEST_CONTENT_LENGTH_PARTIAL_HEADER_VALUE ) - 2U )

/* Template HTTP PUT response. This has no body. */
#define HTTP_TEST_RESPONSE_PUT \
Expand Down Expand Up @@ -235,6 +251,9 @@ static HTTPRequestHeaders_t requestHeaders = { 0 };
/* Header parsing callback shared among the tests. */
static HTTPClient_ResponseHeaderParsingCallback_t headerParsingCallback = { 0 };

/* Flag to indicate this callback is called. */
static int statusCompleteCallbackFlag = 0;

/* A mocked timer query function that increments on every call. */
static uint32_t getTestTime( void )
{
Expand Down Expand Up @@ -432,6 +451,7 @@ static void helper_parse_status_line( const char ** pNext,
const llhttp_settings_t * pSettings )
{
const char * pReasonPhraseStart = NULL;
const char * pNextLineStart = NULL;
size_t reasonPhraseStartLen = 0;

/* For purposes of unit testing the response is well formed in the non-error
Expand All @@ -440,17 +460,36 @@ static void helper_parse_status_line( const char ** pNext,
* always string literals. strchr() should not be used in application code. */
*pNext = strchr( *pNext, SPACE_CHARACTER ); /* Get the space before the status-code. */
*pNext += SPACE_CHARACTER_LEN;
*pNext = strchr( *pNext, SPACE_CHARACTER ); /* Get the space before the reason-phrase. */
*pNext += SPACE_CHARACTER_LEN;
pReasonPhraseStart = *pNext;
*pNext = strstr( *pNext, HTTP_HEADER_LINE_SEPARATOR );
reasonPhraseStartLen = ( size_t ) ( *pNext - pReasonPhraseStart );
/* pNext points to the status code now. */

pReasonPhraseStart = strchr( *pNext, SPACE_CHARACTER );
pReasonPhraseStart = &( pReasonPhraseStart[ SPACE_CHARACTER_LEN ] );

pNextLineStart = strstr( *pNext, HTTP_HEADER_LINE_SEPARATOR );
pNextLineStart = &( pNextLineStart[ HTTP_HEADER_LINE_SEPARATOR_LEN ] );

pParser->status_code = 200;
pSettings->on_status( pParser,
pReasonPhraseStart,
reasonPhraseStartLen );

*pNext += HTTP_HEADER_LINE_SEPARATOR_LEN;
/* Check if the reason phrase exist in the header and call the corresponding callback.
* Reason phrase "OK" exists in the response "HTTP/1.1 200 OK\r\n". The callback
* on_status is called.
* Reason phrase doesn't exist in the response "HTTP/1.1 200\r\n". The callback
* on_status_complete is called. */
if( pNextLineStart > pReasonPhraseStart )
{
reasonPhraseStartLen = ( size_t ) ( pNextLineStart - pReasonPhraseStart );
reasonPhraseStartLen = reasonPhraseStartLen - HTTP_HEADER_LINE_SEPARATOR_LEN;
pSettings->on_status( pParser,
pReasonPhraseStart,
reasonPhraseStartLen );
*pNext = pNextLineStart;
}
else
{
statusCompleteCallbackFlag = 1;
pSettings->on_status_complete( pParser );
*pNext = pNextLineStart;
}
}

/* Mock helper that parses all of the headers starting from pNext. */
Expand Down Expand Up @@ -805,6 +844,7 @@ void setUp( void )
response.pBuffer = httpBuffer;
response.bufferLen = sizeof( httpBuffer );
response.pHeaderParsingCallback = &headerParsingCallback;
statusCompleteCallbackFlag = 0;

/* Ignore third-party init functions that return void. */
llhttp_init_Ignore();
Expand Down Expand Up @@ -846,6 +886,72 @@ void test_HTTPClient_Send_HEAD_request_parse_whole_response( void )

/*-----------------------------------------------------------*/

/* Test successfully parsing a response to a HEAD request. The full response
* message is present in the response buffer on the first network read. */
void test_HTTPClient_Send_HEAD_request_no_parse_body( void )
{
HTTPStatus_t returnStatus = HTTPSuccess;

llhttp_execute_Stub( llhttp_execute_whole_response );

response.respOptionFlags |= HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG;

returnStatus = HTTPClient_Send( &transportInterface,
&requestHeaders,
NULL,
0,
&response,
0 );
TEST_ASSERT_EQUAL( HTTPSuccess, returnStatus );
TEST_ASSERT_EQUAL( NULL, response.pBody );
TEST_ASSERT_EQUAL( 0U, response.bodyLen );
TEST_ASSERT_EQUAL( response.pBuffer + ( sizeof( HTTP_STATUS_LINE_OK ) - 1U ), response.pHeaders );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_LENGTH - ( sizeof( HTTP_STATUS_LINE_OK ) - 1U ) - HTTP_HEADER_END_INDICATOR_LEN,
response.headersLen );
TEST_ASSERT_EQUAL( HTTP_STATUS_CODE_OK, response.statusCode );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_CONTENT_LENGTH, response.contentLength );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_HEADER_COUNT, response.headerCount );
TEST_ASSERT_BITS_HIGH( HTTP_RESPONSE_CONNECTION_CLOSE_FLAG, response.respFlags );
TEST_ASSERT_BITS_LOW( HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG, response.respFlags );
}

/*-----------------------------------------------------------*/

/* Test successfully parsing a response to a HEAD request. The full response
* message is present in the response buffer on the first network read. The response
* contains a status code but without a reason string. The on_status_complete is called
* in this case. */
void test_HTTPClient_Send_HEAD_request_parse_whole_response_no_reason_string( void )
{
HTTPStatus_t returnStatus = HTTPSuccess;

pNetworkData = ( uint8_t * ) HTTP_TEST_RESPONSE_HEAD_2;
networkDataLen = HTTP_TEST_RESPONSE_HEAD_2_LENGTH;

llhttp_execute_Stub( llhttp_execute_whole_response );

returnStatus = HTTPClient_Send( &transportInterface,
&requestHeaders,
NULL,
0,
&response,
0 );
TEST_ASSERT_EQUAL( HTTPSuccess, returnStatus );
TEST_ASSERT_EQUAL( NULL, response.pBody );
TEST_ASSERT_EQUAL( 0U, response.bodyLen );
TEST_ASSERT_EQUAL( response.pBuffer + ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) - 1U ), response.pHeaders );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_LENGTH - ( sizeof( HTTP_STATUS_LINE_NO_REASON_PHRASE ) - 1U ) - HTTP_HEADER_END_INDICATOR_LEN,
response.headersLen );
TEST_ASSERT_EQUAL( HTTP_STATUS_CODE_OK, response.statusCode );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_CONTENT_LENGTH, response.contentLength );
TEST_ASSERT_EQUAL( HTTP_TEST_RESPONSE_HEAD_2_HEADER_COUNT, response.headerCount );
TEST_ASSERT_BITS_HIGH( HTTP_RESPONSE_CONNECTION_CLOSE_FLAG, response.respFlags );
TEST_ASSERT_BITS_LOW( HTTP_RESPONSE_CONNECTION_KEEP_ALIVE_FLAG, response.respFlags );
TEST_ASSERT_EQUAL( 1, statusCompleteCallbackFlag );
}

/*-----------------------------------------------------------*/

/* Test successfully parsing a response to a PUT request. The full response
* message is present in the response buffer on the first network read. */
void test_HTTPClient_Send_PUT_request_parse_whole_response( void )
Expand Down Expand Up @@ -1757,6 +1863,25 @@ void test_HTTPClient_Send_parsing_errors( void )
0 );
TEST_ASSERT_EQUAL( HTTPSecurityAlertInvalidContentLength, returnStatus );

httpParsingErrno = HPE_PAUSED;
returnStatus = HTTPClient_Send( &transportInterface,
&requestHeaders,
NULL,
0,
&response,
0 );
TEST_ASSERT_EQUAL( HTTPParserPaused, returnStatus );

httpParsingErrno = HPE_PAUSED;
response.respOptionFlags |= HTTP_RESPONSE_DO_NOT_PARSE_BODY_FLAG;
returnStatus = HTTPClient_Send( &transportInterface,
&requestHeaders,
NULL,
0,
&response,
0 );
TEST_ASSERT_EQUAL( HTTPNoResponse, returnStatus );

/* Use -1 to indicate an unknown error. */
httpParsingErrno = -1;
returnStatus = HTTPClient_Send( &transportInterface,
Expand Down
50 changes: 50 additions & 0 deletions test/unit-test/core_http_utest.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ typedef struct _headers
"%s: %s\r\n" \
"%s: %s\r\n\r\n"

#define HTTP_TEST_HEADER_NO_USER_AGENT_FORMAT \
"%s %s %s\r\n" \
"%s: %s\r\n\r\n"

#define HTTP_TEST_EXTRA_HEADER_FORMAT \
"%s %s %s\r\n" \
"%s: %s\r\n" \
Expand All @@ -94,6 +98,19 @@ typedef struct _headers
HTTP_TEST_HOST_VALUE_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \
HTTP_HEADER_LINE_SEPARATOR_LEN )

/* Length of the following template HTTP header.
* <HTTP_METHOD_GET> <HTTP_TEST_REQUEST_PATH> <HTTP_PROTOCOL_VERSION> \r\n
* <HTTP_HOST_FIELD>: <HTTP_TEST_HOST_VALUE> \r\n
* \r\n
* This is used to initialize the expectedHeader string. */
#define HTTP_TEST_PREFIX_HEADER_NO_USER_AGENT_LEN \
( HTTP_METHOD_GET_LEN + SPACE_CHARACTER_LEN + \
HTTP_TEST_REQUEST_PATH_LEN + SPACE_CHARACTER_LEN + \
HTTP_PROTOCOL_VERSION_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \
HTTP_HOST_FIELD_LEN + HTTP_HEADER_FIELD_SEPARATOR_LEN + \
HTTP_TEST_HOST_VALUE_LEN + HTTP_HEADER_LINE_SEPARATOR_LEN + \
HTTP_HEADER_LINE_SEPARATOR_LEN )

/* Add 1 because snprintf(...) writes a null byte at the end. */
#define HTTP_TEST_INITIALIZED_HEADER_BUFFER_LEN \
( HTTP_TEST_PREFIX_HEADER_LEN + 1 )
Expand Down Expand Up @@ -472,6 +489,39 @@ void test_Http_InitializeRequestHeaders_Happy_Path()
expectedHeaders.dataLen );
}

/**
* @brief Test happy path with HTTP_REQUEST_NO_USER_AGENT_FLAG.
*/
void test_Http_InitializeRequestHeaders_no_user_agent_flag()
{
HTTPStatus_t httpStatus = HTTPSuccess;
HTTPRequestHeaders_t requestHeaders = { 0 };
HTTPRequestInfo_t requestInfo = { 0 };
int numBytes = 0;

setupRequestInfo( &requestInfo );
requestInfo.reqFlags |= HTTP_REQUEST_NO_USER_AGENT_FLAG;

expectedHeaders.dataLen = HTTP_TEST_PREFIX_HEADER_NO_USER_AGENT_LEN;
setupBuffer( &requestHeaders );

/* Happy Path testing. */
numBytes = snprintf( ( char * ) expectedHeaders.buffer, sizeof( expectedHeaders.buffer ),
HTTP_TEST_HEADER_NO_USER_AGENT_FORMAT,
HTTP_METHOD_GET, HTTP_TEST_REQUEST_PATH,
HTTP_PROTOCOL_VERSION,
HTTP_HOST_FIELD, HTTP_TEST_HOST_VALUE );
/* Make sure that the entire pre-existing data was printed to the buffer. */
TEST_ASSERT_GREATER_THAN( 0, numBytes );
TEST_ASSERT_LESS_THAN( sizeof( expectedHeaders.buffer ), ( size_t ) numBytes );

httpStatus = HTTPClient_InitializeRequestHeaders( &requestHeaders, &requestInfo );
TEST_ASSERT_EQUAL( HTTPSuccess, httpStatus );
TEST_ASSERT_EQUAL( expectedHeaders.dataLen, requestHeaders.headersLen );
TEST_ASSERT_EQUAL_MEMORY( expectedHeaders.buffer, requestHeaders.pBuffer,
expectedHeaders.dataLen );
}

/**
* @brief Test NULL parameters, following order of else-if blocks in the HTTP library.
*/
Expand Down

0 comments on commit 51d580b

Please sign in to comment.