/*
 * FreeRTOS Secure Sockets V1.1.9
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"

/* Secure Sockets includes. */
#include "iot_secure_sockets.h"

/* Test framework includes. */
#include "unity_fixture.h"
#include "aws_test_runner.h"
#include "aws_test_utils.h"
#include "aws_test_tcp.h"

/* Update this file with port-specific values. */
#include "aws_test_tcp_config.h"

/* Update this file with AWS Credentials. */
#include "aws_clientcredential.h"

/* Verbose printing. */
#define tcptestPRINTF( x )
/* In case of test failures, FAILUREPRINTF may provide more detailed information. */
#define tcptestFAILUREPRINTF( x )    vLoggingPrintf x
/* Fail the test on tcptestASSERT. */
#define tcptestASSERT( x )           configASSERT( x )
/* Take measurements of the FreeRTOS heap before and after every test. */
#define tcptestMEMORYWATCH    0

/* Connection type. Add additional test servers here. */
typedef enum
{
    eSecure,    /* Secure test server.  Uses tcptestECHO_SERVER_TLS. */
    eNonsecure, /* Non secure test server.  Uses tcptestECHO_SERVER. */
    eAwsBroker  /* AWS IoT Broker.  Uses clientcredential. */
} Server_t;

/* The echo tasks create a socket, send out a number of echo requests, listen
 * for the echo reply, then close the socket again before starting over.  This
 * delay is used between each iteration to ensure the network does not get too
 * congested. */
#define tcptestLOOP_DELAY_MS    ( 150 )
#define tcptestLOOP_DELAY       ( ( TickType_t ) tcptestLOOP_DELAY_MS / portTICK_PERIOD_MS )

/* The number of times SOCKETS_Connect_TCP opens and closes a socket. */
/* Need to test at least 20 times. So bugs were not discovered with only 10 loops. */
#define tcptestCONNECT_AND_CLOSE_LOOP    ( 20 )

/* Filler values in the RX and TX buffers used to check for undesirable
 * buffer modification. */
#define tcptestRX_BUFFER_FILLER          0xFF
#define tcptestTX_BUFFER_FILLER          0xAA

/* Size of pcTxBuffer and pcRxBuffer. */
#define tcptestBUFFER_SIZE               ( testrunnerBUFFER_SIZE / 2 )

/* Global buffers are shared between tests to reduce RAM usage. */
char * pcTxBuffer = &cBuffer[ 0 ];
char * pcRxBuffer = &cBuffer[ tcptestBUFFER_SIZE ];

/* Default Rx and Tx time outs are used to ensure the sockets do not
 * wait too long for missing data. */
static const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( integrationtestportableRECEIVE_TIMEOUT );
static const TickType_t xSendTimeOut = pdMS_TO_TICKS( integrationtestportableSEND_TIMEOUT );

/* Primary socket, declared globally so that it can be closed in test tear down
 * in the event that a test exits/fails before socket is closed. */
volatile Socket_t xSocket = SOCKETS_INVALID_SOCKET;
volatile BaseType_t xSocketOpen = pdFALSE;

/*
 * Uses a socket to send more than MSS bytes in one go to the standard echo
 * port number 7.  The echoed data is received on the same socket but in a
 * different task (see prvEchoClientRxTask() below).
 */
static void prvEchoClientTxTask( void * pvParameters );

/* TCP Echo Client tasks multi-task test parameters. These can be configured in aws_test_tcp_config.h. */
#ifndef tcptestTCP_ECHO_TASKS_STACK_SIZE
    #define tcptestTCP_ECHO_TASKS_STACK_SIZE    ( configMINIMAL_STACK_SIZE * 4 )
#endif
#ifndef tcptestTCP_ECHO_TASKS_PRIORITY
    #define tcptestTCP_ECHO_TASKS_PRIORITY      ( tskIDLE_PRIORITY )
#endif

/* The queue used by prvEchoClientTxTask() to send the next socket to use to
 * prvEchoClientRxTask(). */
static volatile QueueHandle_t xSocketPassingQueue = NULL;

/* The event group used by the prvEchoClientTxTask() and prvEchoClientRxTask()
 * to synchronize prior to commencing a cycle using a new socket. */
static volatile EventGroupHandle_t xSyncEventGroup = NULL;


/* Bit definitions used with the xSyncEventGroup event group to allow the
 * prvEchoClientTxTask() and prvEchoClientRxTask() tasks to synchronize before
 * commencing a new cycle with a different socket. */
#define tcptestTX_TASK_BIT    ( 0x01 << 1 )
#define tcptestRX_TASK_BIT    ( 0x01 << 2 )


/* Array setup for 2 times the biggest possible frame (eth0 and WIFI). */
/* WIFI is 2304 and eth0 is 1522. */
#define tcptestTWICE_MAX_FRAME_SIZE    ( 2 * 2304 )
/* Array is defined as const so it can be located in flash. */
static const char cTransmittedString[ tcptestTWICE_MAX_FRAME_SIZE ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 1 };

#ifndef tcptestECHO_TEST_SYNC_TIMEOUT
    #define tcptestECHO_TEST_SYNC_TIMEOUT    80000
#endif

#ifndef tcptestECHO_TEST_RXTX_TIMEOUT
    #define tcptestECHO_TEST_RXTX_TIMEOUT       5000
#endif
#define tcptestECHO_CLIENT_EVENT_MASK           ( ( 1 << tcptestNUM_ECHO_CLIENTS ) - 1 )

#define  tcptestECHO_TEST_SYNC_TIMEOUT_TICKS    ( pdMS_TO_TICKS( tcptestECHO_TEST_SYNC_TIMEOUT ) )
static const TickType_t xEchoTestRxTxTimeOut = pdMS_TO_TICKS( tcptestECHO_TEST_RXTX_TIMEOUT );

/* All the modes for the test  SOCKETS_Echo_Client_Separate_Tasks */
typedef enum
{
    LARGE_BUFFER_HIGH_PRIORITY,
    SMALL_BUFFER_HIGH_PRIORITY,
    LARGE_BUFFER_LOW_PRIORITY,
    SMALL_BUFFER_LOW_PRIORITY,
    tcptestMAX_ECHO_TEST_MODES
} tcptestEchoTestModes_t;

typedef struct
{
    Server_t xConn;
    uint16_t usTaskTag;
    BaseType_t xResult;
    TaskHandle_t xTaskHandle;
} tcptestEchoClientsTaskParams_t;

/* Number of time the test goes through all the modes. */
#define tcptestMAX_LOOPS_ECHO_TEST            tcptestMAX_ECHO_TEST_MODES

#define tcptestECHO_TEST_LOW_PRIORITY         tskIDLE_PRIORITY
#define tcptestECHO_TEST_HIGH_PRIORITY        ( configMAX_PRIORITIES - 1 )

#ifndef ipconfigTCP_MSS
    #define ipconfigTCP_MSS                   ( 256 )
#endif
#define tcptestNUM_ECHO_CLIENTS               ( 2 )
#define tcptestMAX_LOOPS_ECHO_CLIENTS_LOOP    ( 10 )

static void prvThreadSafeDifferentSocketsDifferentTasks( void * pvParameters );
/****************** Unity Test Code *********************************/
size_t xHeapB;
size_t xHeapA;
/* Group declaration required by Unity framework. */
TEST_GROUP( Full_TCP );

/* Setup required by Unity framework. */
TEST_SETUP( Full_TCP )
{
    #if ( tcptestMEMORYWATCH == 1 )
        /* Give the print buffer time to empty */
        vTaskDelay( 500 );
        xHeapB = xPortGetFreeHeapSize();
    #endif
}

/* Tear down required by Unity framework.
 * Closes the global socket if it was left open by the test. */
TEST_TEAR_DOWN( Full_TCP )
{
    int32_t lReturn;

    if( TEST_PROTECT() )
    {
        if( xSocketOpen == pdTRUE )
        {
            lReturn = SOCKETS_Close( xSocket );
            xSocketOpen = pdFALSE;
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, lReturn, "SOCKETS_Close failed in tear down. \r\n" );
        }
    }

    #if ( tcptestMEMORYWATCH == 1 )
        /* Give the print buffer time to empty */
        vTaskDelay( 500 );
        xHeapA = xPortGetFreeHeapSize();

        configPRINTF( ( "Heap before: %d, Heap After: %d \r\n", xHeapB, xHeapA ) );
    #endif
}


/*-----------------------------------------------------------*/
static void prvCreateTxData( char * cBuffer,
                             size_t xMessageLength,
                             uint32_t ulTxCount );
static BaseType_t prvCheckRxTxBuffers( uint8_t * pucTxBuffer,
                                       uint8_t * pucRxBuffer,
                                       size_t xMessageLength );

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

/* Creates a TCP Socket.
 * Causes a test failure if socket creation fails. */
static Socket_t prvTcpSocketHelper( volatile BaseType_t * pxSocketOpen )
{
    Socket_t xSocket;

    /* Make socket. */
    xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                              SOCKETS_SOCK_STREAM,
                              SOCKETS_IPPROTO_TCP );

    if( xSocket != SOCKETS_INVALID_SOCKET )
    {
        /* Test variable for closing exits/fails before closing. */
        *pxSocketOpen = pdTRUE;
    }

    return xSocket;
}


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

/* Sets secure socket options and returns the address of the secure echo server. */
static BaseType_t prvSecureConnectHelper( Socket_t xSocket,
                                          SocketsSockaddr_t * pxHostAddress )
{
    BaseType_t xResult = pdFAIL;

    /* Echo requests are sent to the secure echo server and port. */
    pxHostAddress->ulAddress = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_TLS_ADDR0,
                                                        tcptestECHO_SERVER_TLS_ADDR1,
                                                        tcptestECHO_SERVER_TLS_ADDR2,
                                                        tcptestECHO_SERVER_TLS_ADDR3 );
    pxHostAddress->usPort = SOCKETS_htons( tcptestECHO_PORT_TLS );
    pxHostAddress->ucLength = sizeof( SocketsSockaddr_t );
    pxHostAddress->ucSocketDomain = SOCKETS_AF_INET;

    /* Set the socket to use TLS. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_REQUIRE_TLS,
                                  NULL,
                                  ( size_t ) 0 );

    if( xResult == SOCKETS_ERROR_NONE )
    {
        /* Set the socket to use the secure server's root CA cert. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,
                                      SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE,
                                      tcptestECHO_HOST_ROOT_CA,
                                      sizeof( tcptestECHO_HOST_ROOT_CA ) );

        if( xResult != SOCKETS_ERROR_NONE )
        {
            tcptestFAILUREPRINTF( ( "%s: Failed to setSockOpt SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE \r\n", __FUNCTION__ ) );
        }
    }
    else
    {
        tcptestFAILUREPRINTF( ( "%: Failed to setSockOpt SOCKETS_SO_REQUIRE_TLS \r\n", __FUNCTION__ ) );
    }

    return xResult;
}

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

static BaseType_t prvNonSecureConnectHelper( Socket_t xSocket,
                                             SocketsSockaddr_t * pxHostAddress )
{
    /* Echo requests are sent to the echo server.  The echo server is
    * listening to tcptestECHO_PORT on this computer's IP address. */
    pxHostAddress->ulAddress = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_ADDR0,
                                                        tcptestECHO_SERVER_ADDR1,
                                                        tcptestECHO_SERVER_ADDR2,
                                                        tcptestECHO_SERVER_ADDR3 );
    pxHostAddress->usPort = SOCKETS_htons( tcptestECHO_PORT );
    pxHostAddress->ucLength = sizeof( SocketsSockaddr_t );
    pxHostAddress->ucSocketDomain = SOCKETS_AF_INET;

    return SOCKETS_ERROR_NONE;
}

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

static BaseType_t prvAwsIotBrokerConnectHelper( Socket_t xSocket,
                                                SocketsSockaddr_t * pxHostAddress )
{
    BaseType_t xResult = SOCKETS_SOCKET_ERROR;

    /* Resolve the broker endpoint to an IP address. */
    pxHostAddress->ulAddress = SOCKETS_GetHostByName( clientcredentialMQTT_BROKER_ENDPOINT );

    if( pxHostAddress->ulAddress != 0 )
    {
        pxHostAddress->usPort = SOCKETS_htons( clientcredentialMQTT_BROKER_PORT );
        pxHostAddress->ucLength = sizeof( SocketsSockaddr_t );
        pxHostAddress->ucSocketDomain = SOCKETS_AF_INET;

        /* Set the socket to use TLS. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,
                                      SOCKETS_SO_REQUIRE_TLS,
                                      NULL,
                                      ( size_t ) 0 );

        if( xResult != SOCKETS_ERROR_NONE )
        {
            tcptestFAILUREPRINTF( ( "%s: Failed to setSockOpt SOCKETS_SO_REQUIRE_TLS \r\n", __FUNCTION__ ) );
        }
    }

    return xResult;
}
/*-----------------------------------------------------------*/

static BaseType_t prvConnectHelper( Socket_t xSocket,
                                    Server_t xConn )
{
    BaseType_t xResult = pdFAIL;
    SocketsSockaddr_t xAddress;

    if( xConn == eSecure )
    {
        xResult = prvSecureConnectHelper( xSocket, &xAddress );
    }
    else if( xConn == eNonsecure )
    {
        xResult = prvNonSecureConnectHelper( xSocket, &xAddress );
    }
    else if( xConn == eAwsBroker )
    {
        xResult = prvAwsIotBrokerConnectHelper( xSocket, &xAddress );
    }
    else
    {
        /* If a delay server is introduced, it will be added here. */
        tcptestFAILUREPRINTF( ( "%s: Failed to setSockOpt SOCKETS_SO_REQUIRE_TLS \r\n", __FUNCTION__ ) );
    }

    if( xResult == SOCKETS_ERROR_NONE )
    {
        uint32_t ulInitialRetryPeriodMs = tcptestLOOP_DELAY_MS;
        BaseType_t xMaxRetries = tcptestRETRY_CONNECTION_TIMES;
        RETRY_EXPONENTIAL( xResult = SOCKETS_Connect( xSocket,
                                                      &xAddress,
                                                      sizeof( xAddress ) ),
                           SOCKETS_ERROR_NONE,
                           ulInitialRetryPeriodMs,
                           xMaxRetries );
    }

    return xResult;
}

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

static BaseType_t prvSetSockOptHelper( Socket_t xSocket,
                                       TickType_t xRxTimeOut,
                                       TickType_t xTxTimeOut )
{
    BaseType_t xResult;

    /* Set a time out so a missing reply does not cause the task to block
     * indefinitely. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_RCVTIMEO,
                                  &xRxTimeOut,
                                  sizeof( xRxTimeOut ) );

    if( xResult == SOCKETS_ERROR_NONE )
    {
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,
                                      SOCKETS_SO_SNDTIMEO,
                                      &xTxTimeOut,
                                      sizeof( xTxTimeOut ) );

        if( xResult != SOCKETS_ERROR_NONE )
        {
            tcptestFAILUREPRINTF( ( "%s: Socket set sock opt send timeout failed \r\n", __FUNCTION__ ) );
        }
    }
    else
    {
        tcptestFAILUREPRINTF( ( "%s: Socket set sock opt receive timeout failed \r\n", __FUNCTION__ ) );
    }

    return xResult;
}

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

static BaseType_t prvConnectHelperWithRetry( volatile Socket_t * pxSocket,
                                             Server_t xConn,
                                             TickType_t xRxTimeOut,
                                             TickType_t xTxTimeOut,
                                             volatile BaseType_t * pxSocketIsAllocated )
{
    BaseType_t xIsConnected = pdFALSE;
    BaseType_t xResult = SOCKETS_ERROR_NONE;
    BaseType_t xRetry = 0;
    SocketsSockaddr_t xEchoServerAddress;
    TickType_t xRetryTimeoutTicks = tcptestLOOP_DELAY;

    do
    {
        /* Create the socket. */
        *pxSocket = prvTcpSocketHelper( pxSocketIsAllocated );

        if( SOCKETS_INVALID_SOCKET == *pxSocket )
        {
            xResult = SOCKETS_SOCKET_ERROR;
            tcptestFAILUREPRINTF( ( "Failed to allocate Socket Descriptor \r\n" ) );
            break;
        }

        /* Set the appropriate socket options for the destination. */
        if( SOCKETS_ERROR_NONE == xResult )
        {
            if( xConn == eSecure )
            {
                xResult = prvSecureConnectHelper( *pxSocket, &xEchoServerAddress );
            }
            else if( xConn == eNonsecure )
            {
                xResult = prvNonSecureConnectHelper( *pxSocket, &xEchoServerAddress );
            }
            else if( xConn == eAwsBroker )
            {
                xResult = prvAwsIotBrokerConnectHelper( *pxSocket, &xEchoServerAddress );
            }
            else
            {
                /* If a delay server is introduced, it will be added here. */
                tcptestFAILUREPRINTF( ( "%s: Failed to setSockOpt SOCKETS_SO_REQUIRE_TLS \r\n", __FUNCTION__ ) );
            }
        }

        /* Set socket timeout options. */
        if( SOCKETS_ERROR_NONE == xResult )
        {
            xResult = prvSetSockOptHelper( *pxSocket, xRxTimeOut, xTxTimeOut );
        }

        /* Attempt, with possible retries, to connect to the destination. */
        if( SOCKETS_ERROR_NONE == xResult )
        {
            xResult = SOCKETS_Connect( *pxSocket,
                                       &xEchoServerAddress,
                                       sizeof( xEchoServerAddress ) );

            if( SOCKETS_ERROR_NONE == xResult )
            {
                xIsConnected = pdTRUE;
            }
            else
            {
                if( xRetry < tcptestRETRY_CONNECTION_TIMES )
                {
                    SOCKETS_Close( *pxSocket );
                    *pxSocket = SOCKETS_INVALID_SOCKET;
                    xResult = SOCKETS_ERROR_NONE;
                    xRetry++;
                    vTaskDelay( xRetryTimeoutTicks );
                    /* Exponentially backoff the retry times */
                    xRetryTimeoutTicks *= 2; /*Arbitrarily chose 2*/
                }
                else
                {
                    /* Too many failures. Give up. */
                    break;
                }
            }
        }
    }
    while( pdFALSE == xIsConnected );

    return xResult;
}

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

static BaseType_t prvSendHelper( Socket_t xSocket,
                                 uint8_t * pucTxBuffer,
                                 size_t xLength )
{
    BaseType_t xNumBytesSentTotal;
    BaseType_t xNumBytes;
    BaseType_t xResult;

    xResult = pdPASS;
    xNumBytesSentTotal = 0;

    while( ( size_t ) xNumBytesSentTotal < xLength )
    {
        xNumBytes = SOCKETS_Send( xSocket,                            /* The socket being sent to. */
                                  &pucTxBuffer[ xNumBytesSentTotal ], /* The data being sent. */
                                  xLength - xNumBytesSentTotal,       /* The length of the data being sent. */
                                  0 );                                /* No flags. */

        if( xNumBytes <= 0 )
        {
            xResult = pdFAIL;
            tcptestFAILUREPRINTF( ( "Error in SOCKETS_Send.  Return value: %d \r\n", xNumBytes ) );
            break;
        }

        xNumBytesSentTotal += xNumBytes;
    }

    if( ( size_t ) xNumBytesSentTotal != xLength )
    {
        xResult = pdFAIL;
    }

    return xResult;
}

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

static BaseType_t prvRecvHelper( Socket_t xSocket,
                                 uint8_t * pucRxBuffer,
                                 size_t xLength )
{
    BaseType_t xNumBytesRecvTotal;
    BaseType_t xNumBytes;
    BaseType_t xResult;

    xResult = pdPASS;
    xNumBytesRecvTotal = 0;

    while( ( size_t ) xNumBytesRecvTotal < xLength )
    {
        xNumBytes = SOCKETS_Recv( xSocket,
                                  &pucRxBuffer[ xNumBytesRecvTotal ],
                                  xLength - xNumBytesRecvTotal,
                                  0 );

        if( xNumBytes == 0 )
        {
            tcptestFAILUREPRINTF( ( "Timed out receiving from echo server \r\n" ) );
            xResult = pdFAIL;
            break;
        }
        else if( xNumBytes < 0 )
        {
            tcptestFAILUREPRINTF( ( "Error %d while receiving from echo server\r\n", xNumBytes ) );
            xResult = pdFAIL;
            break;
        }

        xNumBytesRecvTotal += xNumBytes;
    }

    if( ( size_t ) xNumBytesRecvTotal != xLength )
    {
        xResult = pdFAIL;
    }

    return xResult;
}

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

static BaseType_t prvShutdownHelper( Socket_t xSocket )
{
    BaseType_t xResult;

    /* The caller is done with the connected socket; initiate a graceful close:
     * send a FIN packet and wait for the server to stop sending. */
    xResult = SOCKETS_Shutdown( xSocket, SOCKETS_SHUT_RDWR );

    if( 0 == xResult )
    {
        /* Keep calling receive until an error code is returned. */
        do
        {
            xResult = SOCKETS_Recv( xSocket,            /* The socket being received from. */
                                    pcRxBuffer,         /* The buffer into which the received data will be written. */
                                    tcptestBUFFER_SIZE, /* The size of the buffer provided to receive the data. */
                                    0 );
        }
        while( xResult >= 0 );

        xResult = 0;
    }

    return xResult;
}

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

static BaseType_t prvCloseHelper( Socket_t xSocket,
                                  volatile BaseType_t * pxSocketOpen )
{
    BaseType_t xResult;

    xResult = SOCKETS_Close( xSocket );

    *pxSocketOpen = pdFALSE;

    return xResult;
}

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

static void prvCreateTxData( char * cBuffer,
                             size_t xMessageLength,
                             uint32_t ulTxCount )
{
    size_t xCharacter;
    char cChar = '0';

    /* Set the Tx buffer to a known filler value. */
    memset( cBuffer, tcptestTX_BUFFER_FILLER, tcptestBUFFER_SIZE );

    /* Fill the end of the buffer with ascending characters. */
    if( xMessageLength > 25 )
    {
        for( xCharacter = 0; xCharacter < xMessageLength; xCharacter++ )
        {
            cBuffer[ xCharacter ] = cChar;
            cChar++;

            if( cChar > '~' )
            {
                cChar = '0';
            }
        }
    }

    /* Write as much of the identifying string as possible to the buffer. */
    snprintf( cBuffer, xMessageLength, "%u TxRx with of length %u", ( unsigned ) ulTxCount, ( unsigned ) xMessageLength );
}

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

static BaseType_t prvCheckRxTxBuffers( uint8_t * pucTxBuffer,
                                       uint8_t * pucRxBuffer,
                                       size_t xMessageLength )
{
    BaseType_t xReturn;
    size_t xIndex;

    xReturn = pdPASS;

    /* Check that the message received is correct. */
    for( xIndex = 0; xIndex < xMessageLength; xIndex++ )
    {
        if( pucTxBuffer[ xIndex ] != pucRxBuffer[ xIndex ] )
        {
            xReturn = pdFAIL;
            tcptestFAILUREPRINTF( ( "Message bytes received different than those sent. Message Length %d, Byte Index %d \r\n", xMessageLength, xIndex ) );
            tcptestASSERT( xReturn == pdPASS );
        }
    }

    /* Check that neither the Rx nor Tx buffers were modified/overflowed. */
    for( xIndex = xMessageLength; xIndex < tcptestBUFFER_SIZE; xIndex++ )
    {
        if( pucRxBuffer[ xIndex ] != tcptestRX_BUFFER_FILLER )
        {
            xReturn = pdFAIL;
            tcptestFAILUREPRINTF( ( "Rx buffer padding modified byte. Message Length %d, Byte Index %d \r\n", xMessageLength, xIndex ) );
        }

        if( pucTxBuffer[ xIndex ] != tcptestTX_BUFFER_FILLER )
        {
            xReturn = pdFAIL;
            tcptestFAILUREPRINTF( ( "Tx buffer padding modified. Message Length %d, Byte Index %d \r\n", xMessageLength, xIndex ) );
        }
    }

    tcptestASSERT( xReturn == pdPASS );

    return xReturn;
}

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

static BaseType_t prvCheckBufferUnmodified( uint8_t * pucBuffer,
                                            uint8_t ucFiller,
                                            size_t xLength )
{
    BaseType_t xResult;
    size_t xIndex;

    xResult = pdPASS;

    for( xIndex = 0; xIndex < xLength; xIndex++ )
    {
        if( pucBuffer[ xIndex ] != ucFiller )
        {
            xResult = pdFAIL;
            tcptestFAILUREPRINTF( ( "Buffer modified at byte %d \r\n", xIndex ) );
        }
    }

    return xResult;
}

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

static BaseType_t prvCheckTimeout( TickType_t xStartTime,
                                   TickType_t xEndTime,
                                   TickType_t xTimeout )
{
    BaseType_t xResult = pdPASS;

    /* Did the function run at least as long as the timeout?
     * An "under tolerance" may be needed when the application timer
     * and the network timer are sourced from separate clocks. */
    if( ( int32_t ) ( xEndTime - xStartTime ) < ( int32_t ) ( xTimeout - integrationtestportableTIMEOUT_UNDER_TOLERANCE ) )
    {
        xResult = pdFAIL;
        tcptestFAILUREPRINTF( ( "Start Time: %d, End Time: %d, Timeout: %d \n", xStartTime, xEndTime, xTimeout ) );
    }

    /* Did the function exit after a reasonable amount of time?
     * An "over tolerance" is used to allow for overhead surrounding the timeout. */
    if( ( xEndTime - xStartTime ) > ( xTimeout + integrationtestportableTIMEOUT_OVER_TOLERANCE ) )
    {
        xResult = pdFAIL;
        tcptestFAILUREPRINTF( ( "Start Time: %d, End Time: %d, Timeout: %d \n", xStartTime, xEndTime, xTimeout ) );
    }

    return xResult;
}

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

TEST_GROUP_RUNNER( Full_TCP )
{
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_CloseInvalidParams );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_CloseWithoutReceiving );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_ShutdownInvalidParams );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_ShutdownWithoutReceiving );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Recv_On_Unconnected_Socket );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Connect_InvalidParams );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Connect_InvalidAddressLength );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Socket_TCP );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_SetSockOpt_RCVTIMEO );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_NonBlocking_Test );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_SetSockOpt_InvalidParams );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Shutdown );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Close );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Recv_ByteByByte );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_SendRecv_VaryLength );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Socket_ConcurrentCount );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Socket_InvalidInputParams );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Send_Invalid );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Recv_Invalid );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_htons_HappyCase );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_inet_addr_quick_HappyCase );

    #if ( tcptestSECURE_SERVER == 1 )
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_CloseInvalidParams );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_CloseWithoutReceiving );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_ShutdownInvalidParams );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_ShutdownWithoutReceiving );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Recv_On_Unconnected_Socket );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_TRUSTED_SERVER_CERTIFICATE );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_SERVER_NAME_INDICATION );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Connect_InvalidParams );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Connect_InvalidAddressLength );
        /* SECURE_SOCKETS_Socket_TCP DNE. */
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_RCVTIMEO );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_NonBlocking_Test );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_InvalidParams );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Shutdown );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Close );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Recv_ByteByByte );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SendRecv_VaryLength );
        /* SECURE_SOCKETS_Socket_InvalidTooManySockets has not been implemented. */
        /*SECURE_SOCKETS_Socket_InvalidInputParams DNE.*/
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Send_Invalid );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Recv_Invalid );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SockEventHandler );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_NonBlockingConnect );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_TwoSecureConnections );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_SetSecureOptionsAfterConnect );
    #endif /* if ( tcptestSECURE_SERVER == 1 ) */

    /* Thread safety tests */
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Threadsafe_SameSocketDifferentTasks );
    RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Threadsafe_DifferentSocketsDifferentTasks );

    #if ( tcptestSECURE_SERVER == 1 )
        //RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Threadsafe_SameSocketDifferentTasks );
        RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_Threadsafe_DifferentSocketsDifferentTasks );
    #endif /* if ( tcptestSECURE_SERVER == 1 ) */
}

/*-------------------------------------------------------------------*/
/*-----------------------Begin Tests---------------------------------*/
/*-------------------------------------------------------------------*/

static void prvSOCKETS_CloseInvalidParams( Server_t xConn )
{
    BaseType_t xResult;

    /* Try to close with an invalid socket. */
    xResult = SOCKETS_Close( SOCKETS_INVALID_SOCKET );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_EINVAL, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_CloseInvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_CloseInvalidParams( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_CloseInvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_CloseInvalidParams( eSecure );
}

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

static void prvSOCKETS_CloseWithoutReceiving( Server_t xConn )
{
    BaseType_t xResult;

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    /* Send a lot of data to the echo server but never use the recv. */
    xResult = SOCKETS_Send( xSocket, &cTransmittedString, tcptestTWICE_MAX_FRAME_SIZE, 0 );
    TEST_ASSERT_GREATER_THAN_MESSAGE( 0, xResult, "Socket was not able to send" );

    /* Try to close. */
    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}


TEST( Full_TCP, AFQP_SOCKETS_CloseWithoutReceiving )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_CloseWithoutReceiving( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_CloseWithoutReceiving )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_CloseWithoutReceiving( eSecure );
}

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

static void prvSOCKETS_ShutdownInvalidParams( Server_t xConn )
{
    BaseType_t xResult;

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt Failed" );

    xResult = SOCKETS_Shutdown( xSocket, ~0 );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket could shutdown with an invalid param" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    /* Call shutdown with invalid parameters. */
    xResult = SOCKETS_Shutdown( SOCKETS_INVALID_SOCKET, SOCKETS_SHUT_RDWR );
    TEST_ASSERT_EQUAL_MESSAGE( SOCKETS_EINVAL, xResult, "Socket could shutdown with an invalid param" );
}

TEST( Full_TCP, AFQP_SOCKETS_ShutdownInvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_ShutdownInvalidParams( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_ShutdownInvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_ShutdownInvalidParams( eSecure );
}

/*-----------------------------------------------------------*/
static void prvSOCKETS_ShutdownWithoutReceiving( Server_t xConn )
{
    BaseType_t xResult;

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    /* Send a lot of data to the echo server but never use the recv. */
    xResult = SOCKETS_Send( xSocket, &cTransmittedString, tcptestTWICE_MAX_FRAME_SIZE, 0 );
    TEST_ASSERT_GREATER_THAN_MESSAGE( 0, xResult, "Socket was not able to send" );

    xResult = SOCKETS_Shutdown( xSocket, SOCKETS_SHUT_RDWR );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}


TEST( Full_TCP, AFQP_SOCKETS_ShutdownWithoutReceiving )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_ShutdownWithoutReceiving( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_ShutdownWithoutReceiving )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_ShutdownWithoutReceiving( eSecure );
}

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

static void prvSOCKETS_Recv_On_Unconnected_Socket( Server_t xConn )
{
    BaseType_t xResult;
    uint8_t ucBuf;
    volatile BaseType_t xConnectedSocketOpen = pdFALSE;
    volatile Socket_t xConnectedSocket;

    if( TEST_PROTECT() )
    {
        xSocket = prvTcpSocketHelper( &xSocketOpen );
        TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

        xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SetSockOpt Failed" );


        /* We connect another socket. The rational for this is a bug that was experienced in the past
         * where the data would be received  by from another socket. */
        /* Attempt to establish the requested connection. */
        xResult = prvConnectHelperWithRetry( &xConnectedSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xConnectedSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /* Send data from the connected socket. */
        xResult = SOCKETS_Send( xConnectedSocket, &ucBuf, 1, 0 );
        TEST_ASSERT_GREATER_THAN_MESSAGE( 0, xResult, "Socket was not able to send" );

        /* Try to receive on unconnected socket. */
        xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
        TEST_ASSERT_LESS_THAN_MESSAGE( 0, xResult, "Socket was able to receive from unconnected socket" );

        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

        /* Close the other socket. */
        xResult = prvShutdownHelper( xConnectedSocket );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

        xResult = prvCloseHelper( xConnectedSocket, &xConnectedSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
        xConnectedSocketOpen = pdFALSE;
    }

    if( xConnectedSocketOpen == pdTRUE )
    {
        ( void ) prvShutdownHelper( xConnectedSocket );

        xResult = prvCloseHelper( xConnectedSocket, &xConnectedSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close." );
    }
}

TEST( Full_TCP, AFQP_SOCKETS_Recv_On_Unconnected_Socket )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Recv_On_Unconnected_Socket( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Recv_On_Unconnected_Socket )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Recv_On_Unconnected_Socket( eSecure );
}


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

static void prvSOCKETS_Socket_TCP( Server_t xConn )
{
    BaseType_t xResult;

    /* Make TCP socket. */
    xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                              SOCKETS_SOCK_STREAM,
                              SOCKETS_IPPROTO_TCP );

    TEST_ASSERT_NOT_EQUAL( xSocket, SOCKETS_INVALID_SOCKET );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_Socket_TCP )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Socket_TCP( eNonsecure );
}

/* Since no connection is made, a secure version of this test is not needed. */

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

static void prvSOCKETS_SetSockOpt_RCVTIMEO( Server_t xConn )
{
    BaseType_t xResult;
    UBaseType_t uxOldPriority;
    TickType_t xStartTime;
    TickType_t xEndTime;
    TickType_t xTimeout;
    TickType_t xTimeouts[] = { 30, 100, 10000 }; /* TODO: Add 0, nonblocking tests */
    uint8_t ucBuffer;
    BaseType_t xIndex;


    xResult = pdPASS;
    tcptestPRINTF( ( "Starting %s \r\n", __FUNCTION__ ) );
    tcptestPRINTF( ( "This tests timeouts, so it takes a while! \r\n" ) );

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    /* Record the priority of this task. */
    uxOldPriority = uxTaskPriorityGet( NULL );

    /*
     * Set this task to be the highest possible priority.
     * Since we are testing timeouts, we don't want other tasks
     * running in the middle of it.
     */
    vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );

    /*
     * Set the receive timeout, check the tick count,
     * call receive (with no data sent), check the tick
     * count again, make sure time elapsed is within
     * time expected.
     */
    for( xIndex = 0; xIndex < sizeof( xTimeouts ) / sizeof( TickType_t ); xIndex++ )
    {
        xTimeout = pdMS_TO_TICKS( xTimeouts[ xIndex ] );
        xResult = SOCKETS_SetSockOpt( xSocket, 0, SOCKETS_SO_RCVTIMEO, &xTimeout, sizeof( TickType_t ) );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to set receive timeout" );

        xStartTime = xTaskGetTickCount();
        xResult = SOCKETS_Recv( xSocket, &ucBuffer, 1, 0 );
        xEndTime = xTaskGetTickCount();
        TEST_ASSERT_LESS_THAN_MESSAGE( 1, xResult, "Receive call failed in receive timeout test" );
        xResult = prvCheckTimeout( xStartTime, xEndTime, xTimeout );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Receive timeout was outside of acceptable range" );
    }

    /* Restore the old priority of this task. */
    vTaskPrioritySet( NULL, uxOldPriority );
    xResult = prvShutdownHelper( xSocket );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}


TEST( Full_TCP, AFQP_SOCKETS_SetSockOpt_RCVTIMEO )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SetSockOpt_RCVTIMEO( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_RCVTIMEO )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SetSockOpt_RCVTIMEO( eSecure );
}
/*-----------------------------------------------------------*/

static void prvSOCKETS_NonBlocking_Test( Server_t xConn )
{
    BaseType_t xResult;
    int32_t lNumBytes;
    TickType_t xStartTime;
    TickType_t xEndTime;
    TickType_t xTimeout = 0;
    TickType_t xWaitTime = 1000;
    uint8_t * pucTxBuffer = ( uint8_t * ) pcTxBuffer;
    uint8_t * pucRxBuffer = ( uint8_t * ) pcRxBuffer;
    size_t xMessageLength = 1200;
    size_t xNumBytesReceived = 0;


    xResult = pdPASS;
    tcptestPRINTF( ( "Starting %s \r\n", __FUNCTION__ ) );
    tcptestPRINTF( ( "This tests timeouts, so it takes a while! \r\n" ) );

    if( TEST_PROTECT() )
    {
        /* Attempt to establish the requested connection. */
        xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /*
         * Set the receive timeout, check the tick count,
         * call receive (with no data sent), check the tick
         * count again, make sure time elapsed is within
         * time expected.
         */
        xResult = SOCKETS_SetSockOpt( xSocket, 0, SOCKETS_SO_NONBLOCK, &xTimeout, sizeof( TickType_t ) );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to set receive timeout" );

        xStartTime = xTaskGetTickCount();
        xResult = SOCKETS_Recv( xSocket, &pucRxBuffer, 1, 0 );
        xEndTime = xTaskGetTickCount();
        TEST_ASSERT_LESS_THAN_MESSAGE( 1, xResult, "Receive call failed in receive timeout test" );

        xResult = prvCheckTimeout( xStartTime, xEndTime, xTimeout );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Receive timeout was outside of acceptable range" );

        /*
         * Send data and receive data in nonblocking mode
         */
        memset( pucTxBuffer, tcptestTX_BUFFER_FILLER, tcptestBUFFER_SIZE );
        prvCreateTxData( ( char * ) pucTxBuffer, xMessageLength, 0 );

        xResult = prvSendHelper( xSocket, pucTxBuffer, xMessageLength );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Data failed to send\r\n" );

        memset( pucRxBuffer, tcptestRX_BUFFER_FILLER, tcptestBUFFER_SIZE );
        xStartTime = xTaskGetTickCount();
        xEndTime = xTaskGetTickCount();

        do
        {
            lNumBytes = SOCKETS_Recv( xSocket, &pucRxBuffer[ xNumBytesReceived ], xMessageLength - xNumBytesReceived, 0 );

            if( lNumBytes > 0 )
            {
                /*
                 * If xResult is negative, it's an error code. Because xNumBytesReceived is an
                 * unsigned int, it will become a large positive number, so the while loop
                 * would end prematurely. This if statement prevents that.
                 */
                xNumBytesReceived += lNumBytes;
            }

            xEndTime = xTaskGetTickCount();
        }
        while( ( ( xEndTime - xStartTime ) < xWaitTime ) && ( xMessageLength > xNumBytesReceived ) );

        TEST_ASSERT_EQUAL_INT32_MESSAGE( xMessageLength, xNumBytesReceived, "Data was not received \r\n" );

        xResult = prvCheckRxTxBuffers( pucTxBuffer, pucRxBuffer, xMessageLength );

        xResult = prvShutdownHelper( xSocket );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );
    }

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}


TEST( Full_TCP, AFQP_SOCKETS_NonBlocking_Test )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_NonBlocking_Test( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_NonBlocking_Test )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_NonBlocking_Test( eSecure );
}
/*-----------------------------------------------------------*/

static void prvSetSockOpt_InvalidParams( Server_t xConn )
{
    BaseType_t xResult;

    if( TEST_PROTECT() )
    {
        /* Attempt to establish the requested connection. */
        xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /* Try to set the invalid option. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,    /* lLevel - Not used. */
                                      -1,   /* Invalid option name. */
                                      NULL, /* pvOptionValue - This is insignificant as the option name is invalid. */
                                      0     /* xOptionLength - zero for NULL value. */
                                      );

        /* Since the option name supplied in the previous
         * call was invalid, we expect the call to the API
         * SOCKETS_SetSockOpt to fail. */
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "SOCKETS_SetSockOpt with invalid option succeeded." );

        /* Try to set a valid option on an invalid socket. */
        xResult = SOCKETS_SetSockOpt( SOCKETS_INVALID_SOCKET, /* Invalid socket. */
                                      0,                      /* lLevel - Not used. */
                                      SOCKETS_SO_RCVTIMEO,    /* Receive timeout. */
                                      &( xReceiveTimeOut ),
                                      sizeof( TickType_t ) );

        /* Since the socket passed in the previous call was
         * invalid, we expect the call to the API
         * SOCKETS_SetSockOpt to fail. */
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "SOCKETS_SetSockOpt with invalid socket succeeded." );
    }

    /* If the code reaches here, it means that the socket
     * was created successfully. Make sure to free it even
     * if any of the above assertion fails. This is needed
     * to avoid leaks. */
    xResult = prvCloseHelper( xSocket, &( xSocketOpen ) );

    /* If this fails there is nothing much we can do
     * to avoid leaking. */
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SOCKETS_Close failed. \r\n" );
}

TEST( Full_TCP, AFQP_SOCKETS_SetSockOpt_InvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSetSockOpt_InvalidParams( eNonsecure );

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_InvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSetSockOpt_InvalidParams( eSecure );

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}
/*-----------------------------------------------------------*/

static void prvSOCKETS_SetSockOpt_SNDTIMEO( Server_t xConn )
{
    /* TODO: This is a stub function. */
    TEST_FAIL_MESSAGE( "This test is not implemented." );
}

TEST( Full_TCP, AFQP_SOCKETS_SetSockOpt_SNDTIMEO )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SetSockOpt_SNDTIMEO( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_SNDTIMEO )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SetSockOpt_SNDTIMEO( eSecure );
}

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

static void prvSOCKETS_Shutdown( Server_t xConn )
{
    BaseType_t xResult;
    uint8_t ucBuf;

    xResult = pdPASS;

    /* Shutdown: Read. */

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = SOCKETS_Shutdown( xSocket, SOCKETS_SHUT_RD );
    TEST_ASSERT_EQUAL_INT32( SOCKETS_ERROR_NONE, xResult );

    do
    {
        xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
    }
    while( xResult >= 0 );

    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* Shutdown: Write. */

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    /* AN: Should this test also make a send before closing the socket,
     * and make sure that the data is received "gracefully"? */

    xResult = SOCKETS_Shutdown( xSocket, SOCKETS_SHUT_WR );
    TEST_ASSERT_EQUAL_INT32( SOCKETS_ERROR_NONE, xResult );

    /* Expected behavior of send after Shutdown/WRITE is ambiguous.
     * do
     * {
     *  xResult = SOCKETS_Send( xSocket, &ucBuf, 1, 0 );
     * }
     * while( xResult >= 0 );
     * TEST_ASSERT_LESS_THAN_INT32( 0, xResult );
     */

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* Shutdown: Read/Write. */

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = SOCKETS_Shutdown( xSocket, SOCKETS_SHUT_RDWR );
    TEST_ASSERT_EQUAL_INT32( SOCKETS_ERROR_NONE, xResult );

    do
    {
        xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
    }
    while( xResult >= 0 );

    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_Shutdown )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Shutdown( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Shutdown )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Shutdown( eSecure );
}

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

static void prvTestSOCKETS_Close( Server_t xConn )
{
    BaseType_t xResult;
    uint8_t ucBuf;

    xResult = pdPASS;

    /* Close an unconnected socket */
    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* The secure sockets API abstraction requires that Socket_t be treated as
     * opaque. It could be a pointer, a handle, an index, or whatever. In addition,
     * SOCKETS_Close treats the socket only as input. Therefore, it is not a valid
     * test to pass a socket that has been freed, via explicit close, to other
     * downstream socket APIs. */
    xSocket = SOCKETS_INVALID_SOCKET;

    /* Closed socket should not connect, send or receive */
    xResult = SOCKETS_Send( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );
    xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );

    /* Close a connected socket */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* See comment above. */
    xSocket = SOCKETS_INVALID_SOCKET;

    /* Closed socket should not connect, send or receive */
    xResult = SOCKETS_Send( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );
    xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );

    /* Close a shutdown socket */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = prvShutdownHelper( xSocket );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* See comment above. */
    xSocket = SOCKETS_INVALID_SOCKET;

    /* Closed socket should not connect, send or receive */
    xResult = SOCKETS_Send( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );
    xResult = SOCKETS_Recv( xSocket, &ucBuf, 1, 0 );
    TEST_ASSERT_LESS_THAN_INT32( 0, xResult );

    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SOCKETS_Close )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTestSOCKETS_Close( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Close )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTestSOCKETS_Close( eSecure );
}


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

static void prvTestSOCKETS_Recv_ByteByByte( Server_t xConn )
{
    BaseType_t xResult = pdFAIL;
    uint32_t ulTxCount;
    size_t xBytesReceived;
    uint8_t * pucTxBuffer = ( uint8_t * ) pcTxBuffer;
    uint8_t * pucRxBuffer = ( uint8_t * ) pcRxBuffer;
    size_t xMessageLengths[] = { 2, 7, 8, 9, 20 };

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    for( ulTxCount = 0; ulTxCount < sizeof( xMessageLengths ) / sizeof( xMessageLengths[ 0 ] ); ulTxCount++ )
    {
        xBytesReceived = 0;

        prvCreateTxData( ( char * ) pucTxBuffer, xMessageLengths[ ulTxCount ], ulTxCount );
        xResult = prvSendHelper( xSocket, pucTxBuffer, xMessageLengths[ ulTxCount ] );

        while( ( xResult == pdPASS ) && ( xBytesReceived < xMessageLengths[ ulTxCount ] ) )
        {
            memset( pucRxBuffer, tcptestRX_BUFFER_FILLER, tcptestBUFFER_SIZE );
            xResult = prvRecvHelper( xSocket, pucRxBuffer, 1 );

            if( xResult == pdPASS )
            {
                if( pucRxBuffer[ 0 ] == pucTxBuffer[ xBytesReceived ] )
                {
                    /* Check that all but the first byte of the RxBuffer is unmodified. */
                    xResult = prvCheckBufferUnmodified( pucRxBuffer + 1,
                                                        tcptestRX_BUFFER_FILLER,
                                                        tcptestBUFFER_SIZE - 1 );
                }
                else
                {
                    xResult = pdFAIL;
                    tcptestPRINTF( ( "Byte %d was incorrectly received\r\n", ( xBytesReceived + 1 ) ) );
                }
            }

            xBytesReceived++;
        }
    }

    /* Close this socket before looping back to create another. */
    TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Failed received" );
    xResult = prvShutdownHelper( xSocket );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_Recv_ByteByByte )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTestSOCKETS_Recv_ByteByByte( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Recv_ByteByByte )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTestSOCKETS_Recv_ByteByByte( eSecure );
}

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

static void prvSOCKETS_SendRecv_VaryLength( Server_t xConn )
{
    BaseType_t xResult;
    uint32_t ulIndex;
    uint32_t ulTxCount;
    const uint32_t ulMaxLoopCount = 10;
    uint32_t ulI;
    uint8_t * pucTxBuffer = ( uint8_t * ) pcTxBuffer;
    uint8_t * pucRxBuffer = ( uint8_t * ) pcRxBuffer;
    size_t xMessageLengths[] = { 1, 2, 7, 8, 9, 1199, 1200, 1201 }; /* TODO: Add 0, send sizes larger than MTU. */

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    xResult = pdPASS;
    ulTxCount = 0;

    for( ulIndex = 0; ulIndex < sizeof( xMessageLengths ) / sizeof( size_t ); ulIndex++ )
    {
        /* Attempt to establish the requested connection. */
        xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /* Send each message length ulMaxLoopCount times. */
        for( ulI = 0; ulI < ulMaxLoopCount; ulI++ )
        {
            memset( pucTxBuffer, tcptestTX_BUFFER_FILLER, tcptestBUFFER_SIZE );

            prvCreateTxData( ( char * ) pucTxBuffer,
                             xMessageLengths[ ulIndex ],
                             ulTxCount );
            ulTxCount++;

            xResult = prvSendHelper( xSocket,
                                     pucTxBuffer,
                                     xMessageLengths[ ulIndex ] );

            TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Data failed to send\r\n" );
            memset( pucRxBuffer, tcptestRX_BUFFER_FILLER, tcptestBUFFER_SIZE );
            xResult = prvRecvHelper( xSocket,
                                     pucRxBuffer,
                                     xMessageLengths[ ulIndex ] );

            TEST_ASSERT_EQUAL_INT32_MESSAGE( pdPASS, xResult, "Data was not received \r\n" );
            xResult = prvCheckRxTxBuffers( pucTxBuffer,
                                           pucRxBuffer,
                                           xMessageLengths[ ulIndex ] );
        }

        xResult = prvShutdownHelper( xSocket );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

        /* Print feedback since this test takes a while! */
        tcptestPRINTF( ( " Sending messages with length %d complete\r\n", xMessageLengths[ ulIndex ] ) );

        /* Pause for a short while to ensure the network is not too
         * congested. */
        vTaskDelay( tcptestLOOP_DELAY );
    }

    /* Report Test Results. */
    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SOCKETS_SendRecv_VaryLength )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SendRecv_VaryLength( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SendRecv_VaryLength )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_SendRecv_VaryLength( eSecure );
}

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

static void prvSOCKETS_Socket_InvalidInputParams( Server_t xConn )
{
    BaseType_t xResult;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* Providing invalid domain. */
    if( TEST_PROTECT() )
    {
        xSocket = SOCKETS_Socket( ( SOCKETS_AF_INET + 1 ),
                                  SOCKETS_SOCK_STREAM,
                                  SOCKETS_IPPROTO_TCP );

        /* If the test code reaches here, it failed. */
        TEST_FAIL_MESSAGE( "Invalid socket domain accepted" );
        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
    }

    /* Providing invalid type. */
    if( TEST_PROTECT() )
    {
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                                  ( SOCKETS_SOCK_STREAM | SOCKETS_SOCK_DGRAM ),
                                  SOCKETS_IPPROTO_TCP );

        TEST_FAIL_MESSAGE( "Invalid socket type accepted" );
        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
    }

    /* Providing invalid protocol. */
    if( TEST_PROTECT() )
    {
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                                  SOCKETS_SOCK_STREAM,
                                  ( SOCKETS_IPPROTO_TCP | SOCKETS_IPPROTO_UDP ) );

        TEST_FAIL_MESSAGE( "Invalid socket protocol accepted" );
        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
    }

    /* Creating a UPD socket. UDP is unsupported. */
    if( TEST_PROTECT() )
    {
        /* Mixing DGRAM type with TCP protocol (instead of UDP). */
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                                  SOCKETS_SOCK_DGRAM,
                                  SOCKETS_IPPROTO_UDP );

        TEST_FAIL_MESSAGE( "Invalid socket created - UDP is not supported. " );
        prvCloseHelper( xSocket, &xSocketOpen );
    }

    if( TEST_PROTECT() )
    {
        /* Mixing DGRAM type with TCP protocol (instead of UDP). */
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                                  SOCKETS_SOCK_DGRAM,
                                  SOCKETS_IPPROTO_TCP );

        TEST_FAIL_MESSAGE( "Invalid socket created - mixed TCP with DGRAM " );
        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
    }

    /* Mixing STREAM type with UDP protocol (instead of TCP). */
    if( TEST_PROTECT() )
    {
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET,
                                  SOCKETS_SOCK_STREAM,
                                  SOCKETS_IPPROTO_UDP );

        TEST_FAIL_MESSAGE( "Invalid socket created - mixed UDP with STREAM" );
        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
    }

    /* Report Test Results. */
    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SOCKETS_Socket_InvalidInputParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Socket_InvalidInputParams( eNonsecure );
}

/* No secure stub needed here since there are no secure input params. */

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

/*
 * Verify the number of sockets that can be concurrently
 * created up to a configured maximum.  Return pdFAIL if
 * fewer than that number are created.  If more than the
 * maximum can be created, a warning is printed, but this
 * is not a failure condition.  Some ports are limited only
 * by free memory. For these ports, check that a reasonable
 * number of sockets can be created concurrently.
 */
#ifdef integrationtestportableMAX_NUM_UNSECURE_SOCKETS
    #define MAX_NUM_SOCKETS    integrationtestportableMAX_NUM_UNSECURE_SOCKETS
#else
    #define MAX_NUM_SOCKETS    5u
#endif
static void prvSOCKETS_Socket_ConcurrentCount( Server_t xConn )
{
    BaseType_t xResult = pdFAIL;
    Socket_t xCreatedSockets[ MAX_NUM_SOCKETS ];
    BaseType_t xSocketsCreated;
    Socket_t xSocket;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    xResult = pdPASS;

    for( xSocketsCreated = 0; xSocketsCreated < MAX_NUM_SOCKETS; xSocketsCreated++ )
    {
        xSocket = SOCKETS_Socket( SOCKETS_AF_INET, SOCKETS_SOCK_STREAM, SOCKETS_IPPROTO_TCP );

        if( xSocket == SOCKETS_INVALID_SOCKET )
        {
            xResult = pdFAIL;
            tcptestPRINTF( ( "%s failed creating a socket number %d \r\n", __FUNCTION__, xSocketsCreated ) );
            break;
        }
        else
        {
            xCreatedSockets[ xSocketsCreated ] = xSocket;
        }
    }

    #ifdef integrationtestportableMAX_NUM_UNSECURE_SOCKETS
        if( xResult == pdPASS )
        {
            xSocket = SOCKETS_Socket( SOCKETS_AF_INET, SOCKETS_SOCK_STREAM, SOCKETS_IPPROTO_TCP );

            if( xSocket != SOCKETS_INVALID_SOCKET )
            {
                SOCKETS_Close( xSocket );
                tcptestPRINTF( ( "%s exceeded maximum number of sockets (%d > %d); is the value of " \
                                 "integrationtestportableMAX_NUM_UNSECURE_SOCKETS correct?\r\n", __FUNCTION__,
                                 ( MAX_NUM_SOCKETS + 1 ), MAX_NUM_SOCKETS ) );
            }
        }
    #endif /* ifdef integrationtestportableMAX_NUM_UNSECURE_SOCKETS */

    /* Cleanup. */
    while( xSocketsCreated > 0 )
    {
        --xSocketsCreated;
        SOCKETS_Close( xCreatedSockets[ xSocketsCreated ] );
    }

    TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Concurrent num sockets test failed" );

    /* Report Test Results. */
    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SOCKETS_Socket_ConcurrentCount )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Socket_ConcurrentCount( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Socket_InvalidTooManySockets )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );
    /* TODO: Modify so that the number of secure sockets can be counted. */
    /*prvSOCKETS_Socket_InvalidTooManySockets( secure ); */
}



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

static void prvSOCKETS_Connect_InvalidParams( Server_t xConn )
{
    BaseType_t xResult;
    SocketsSockaddr_t xEchoServerAddress;

    uint32_t ulEchoServerIP;

    ulEchoServerIP = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_ADDR0,
                                              tcptestECHO_SERVER_ADDR1,
                                              tcptestECHO_SERVER_ADDR2,
                                              tcptestECHO_SERVER_ADDR3 );


    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    /* Echo requests are sent to the echo server.  The echo server is
    * listening to tcptestECHO_PORT on this computer's IP address. */
    xEchoServerAddress.usPort = SOCKETS_htons( tcptestECHO_PORT );
    xEchoServerAddress.ulAddress = ulEchoServerIP;

    /* Invalid socket. */
    xResult = SOCKETS_Connect( SOCKETS_INVALID_SOCKET,
                               &xEchoServerAddress,
                               sizeof( xEchoServerAddress ) );

    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connect on an invalid socket failed" );

    /* Invalid IP address (0.0.0.0).  TODO: Investigate reserved IP addresses */
    xEchoServerAddress.ulAddress = SOCKETS_inet_addr_quick( 0, 0, 0, 0 );
    xResult = SOCKETS_Connect( xSocket,
                               &xEchoServerAddress,
                               sizeof( xEchoServerAddress ) );

    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connect to IP Address 0.0.0.0 worked" );

    /* Invalid port (0). */
    xEchoServerAddress.usPort = SOCKETS_htons( 0 );
    xEchoServerAddress.ulAddress = ulEchoServerIP;
    xResult = SOCKETS_Connect( xSocket,
                               &xEchoServerAddress,
                               sizeof( xEchoServerAddress ) );

    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connect to Port 0 worked" );

    /* NULL Address. */
    xResult = SOCKETS_Connect( xSocket, NULL, 0 );

    /* Ensure that the attempt to connect to NULL address fails. */
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connect to NULL Address worked." );

    /* TODO: Does port 0 mean connect to any available port? */
    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_Connect_InvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Connect_InvalidParams( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Connect_InvalidParams )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Connect_InvalidParams( eSecure );
}

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

static void prvConnect_InvalidAddressLength( Server_t xConn,
                                             uint32_t ulAddressLength )
{
    BaseType_t xResult;
    SocketsSockaddr_t xEchoServerAddress;

    if( TEST_PROTECT() )
    {
        xEchoServerAddress.ucLength = ( uint8_t ) ulAddressLength;
        xEchoServerAddress.ucSocketDomain = ( uint8_t ) SOCKETS_AF_INET;

        if( xConn == eNonsecure )
        {
            /* Populate the non-secure echo server address. */
            xEchoServerAddress.ulAddress = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_ADDR0,
                                                                    tcptestECHO_SERVER_ADDR1,
                                                                    tcptestECHO_SERVER_ADDR2,
                                                                    tcptestECHO_SERVER_ADDR3 );
            xEchoServerAddress.usPort = SOCKETS_htons( tcptestECHO_PORT );
        }
        else if( xConn == eSecure )
        {
            /* Populate the secure echo server address. */
            xEchoServerAddress.ulAddress = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_TLS_ADDR0,
                                                                    tcptestECHO_SERVER_TLS_ADDR1,
                                                                    tcptestECHO_SERVER_TLS_ADDR2,
                                                                    tcptestECHO_SERVER_TLS_ADDR3 );
            xEchoServerAddress.usPort = SOCKETS_htons( tcptestECHO_PORT_TLS );
        }
        else
        {
            /* This test is not designed for AWS IoT Broker. */
            TEST_ABORT();
        }

        /* Create a socket. */
        xSocket = prvTcpSocketHelper( &( xSocketOpen ) );

        /* Ensure that the socket was created successfully. */
        TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed." );
        TEST_ASSERT_EQUAL_MESSAGE( pdTRUE, xSocketOpen, "Socket creation failed." );

        /* Connect to the echo server but instead of calculating
         * the length of xEchoServerAddress, specify the length
         * supplied as parameter to this function for the xAddressLength
         * field. Since xAddressLength field of SOCKETS_Connect is
         * currently ignored, we expect the connect to succeed.
         */
        xResult = SOCKETS_Connect( xSocket, &( xEchoServerAddress ), ulAddressLength );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket connect failed." );
    }

    /* If a socket was created, make sure to free it even
     * if any of the above assertion fails. This is needed
     * to avoid leaks. */
    if( xSocketOpen == pdTRUE )
    {
        xResult = prvCloseHelper( xSocket, &( xSocketOpen ) );

        /* If this fails there is nothing much we can do
         * to avoid leaking. */
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SOCKETS_Close failed. \r\n" );
    }
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SOCKETS_Connect_InvalidAddressLength )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* AddressLength 0. */
    prvConnect_InvalidAddressLength( eNonsecure, 0 );

    /* AddressLength 100. */
    prvConnect_InvalidAddressLength( eNonsecure, 100 );

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Connect_InvalidAddressLength )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* AddressLength 0. */
    prvConnect_InvalidAddressLength( eSecure, 0 );

    /* AddressLength 100. */
    prvConnect_InvalidAddressLength( eSecure, 100 );

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}
/*-----------------------------------------------------------*/

static void prvSend_Invalid( Server_t xConn )
{
    BaseType_t xResult;
    uint8_t * pucTxBuffer = ( uint8_t * ) pcTxBuffer;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* Unconnected socket. */
    if( TEST_PROTECT() )
    {
        /* Attempt to establish the requested connection. */
        xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /* Invalid socket. */
        xResult = SOCKETS_Send( SOCKETS_INVALID_SOCKET, pucTxBuffer, 300, 0 );
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Socket send fails with invalid socket" );

        /* NULL Tx Buffer. */

        xResult = SOCKETS_Send( xSocket, NULL, 300, 0 );
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Socket send should return error for NULL input buffer" );
    }
    else
    {
        TEST_FAIL_MESSAGE( "Sending on an TCP socket with NULL buffer triggered an assert " );
    }

    /* Cleanup. */
    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* Unconnected socket. */
    if( TEST_PROTECT() )
    {
        xSocket = prvTcpSocketHelper( &xSocketOpen );
        TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

        xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt failed" );
        xResult = SOCKETS_Send( xSocket, pucTxBuffer, 300, 0 );
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Socket send worked with an unconnected socket" );

        xResult = prvCloseHelper( xSocket, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket close failed" );
    }
    else
    {
        TEST_FAIL_MESSAGE( "Sending on an unconnected TCP socket triggered an assert " );
    }

    /* Report Test Results. */
    tcptestPRINTF( ( "%s passed\r\n", __FUNCTION__ ) );
}

TEST( Full_TCP, AFQP_SOCKETS_Send_Invalid )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSend_Invalid( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Send_Invalid )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSend_Invalid( eSecure );
}

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

static void prvRecv_Invalid( Server_t xConn )
{
    BaseType_t xResult;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, xConn, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    /* Receive with NULL buffer should fail. */
    xResult = SOCKETS_Recv( xSocket, NULL, 100, 0 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Socket receive with NULL receive buffer should have triggered error" );

    /* Receive on invalid socket should fail. */
    xResult = SOCKETS_Recv( SOCKETS_INVALID_SOCKET, pcRxBuffer, tcptestBUFFER_SIZE, 0 );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Socket receive with invalid socket should have triggered error" );

    /* Cleanup. */
    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SOCKETS_Recv_Invalid )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvRecv_Invalid( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Recv_Invalid )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvRecv_Invalid( eSecure );
}

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

static void prvServerDomainName( void )
{
    BaseType_t xResult;
    SocketsSockaddr_t xAwsBrokerAddress;

    /* TODO: Generically change this. */
    char cRealAddress[ sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) ];
    char cFakeAddress[ sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) ];

    memcpy( cRealAddress, clientcredentialMQTT_BROKER_ENDPOINT, sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) );
    memcpy( cFakeAddress, cRealAddress, sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) );
    /* .com -> .cpm */
    cFakeAddress[ sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) - 3 ]++;


    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt Failed" );

    /* Give the client a valid subject name to check against. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_SERVER_NAME_INDICATION,
                                  cRealAddress,
                                  sizeof( cRealAddress ) );
    xResult = prvConnectHelper( xSocket, eAwsBroker );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt Failed" );

    /* Give the client an INVALID subject name to check against. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_SERVER_NAME_INDICATION,
                                  cFakeAddress,
                                  sizeof( cFakeAddress ) );

    /* Get the address struct for AWS Broker and SetSockOpt REQUIRE_TLS. */
    xResult = prvAwsIotBrokerConnectHelper( xSocket, &xAwsBrokerAddress );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed setup AWS Broker address struct." );

    xResult = SOCKETS_Connect( xSocket, &xAwsBrokerAddress, sizeof( SocketsSockaddr_t ) );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connect worked when subject name check should have failed it." );

    /* Server unverified indicates a connection
     *  was established without verifying the server. */
    if( xResult != SOCKETS_TLS_HANDSHAKE_ERROR )
    {
        xResult = SOCKETS_Send( xSocket, pcTxBuffer, 100, 0 );
        TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Send allowed after subject name connection failure." );
    }

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_SERVER_NAME_INDICATION )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvServerDomainName();
}

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

static void prvTrustedServerCertificate( void )
{
    BaseType_t xResult = pdFAIL;
    SocketsSockaddr_t xSecureEchoServerAddress;

    /* Attempt to establish the requested connection. */
    xResult = prvConnectHelperWithRetry( &xSocket, eSecure, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );

    /* Repeat the steps above, but without using SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE.
     * Make sure that an error is returned. */
    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt Failed" );

    /* Set the socket to use TLS. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_REQUIRE_TLS,
                                  NULL,
                                  ( size_t ) 0 );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SetSockOpt to require TLS failed" );

    /* Echo requests are sent to the secure echo server and port. */
    xSecureEchoServerAddress.ulAddress = SOCKETS_inet_addr_quick( tcptestECHO_SERVER_TLS_ADDR0,
                                                                  tcptestECHO_SERVER_TLS_ADDR1,
                                                                  tcptestECHO_SERVER_TLS_ADDR2,
                                                                  tcptestECHO_SERVER_TLS_ADDR3 );

    xSecureEchoServerAddress.usPort = SOCKETS_htons( tcptestECHO_PORT_TLS );

    xResult = SOCKETS_Connect( xSocket, &xSecureEchoServerAddress, sizeof( xSecureEchoServerAddress ) );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connection permitted with untrusted server CA cert" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSockOpt_TRUSTED_SERVER_CERTIFICATE )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTrustedServerCertificate();
}


/* This test is different than the previous test because it causes the
 * SL_SSL_NOTIFICATION_WRONG_ROOT_CA asynchronous event to be hit.
 */
static void prvTriggerWrongRootCA( void )
{
    BaseType_t xResult = pdFAIL;
    SocketsSockaddr_t xAwsIotBrokerAddress;

    /* Repeat the steps above, but without using SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE.
     * Make sure that an error is returned. */
    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );
    xResult = prvSetSockOptHelper( xSocket, xReceiveTimeOut, xSendTimeOut );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Set sockopt Failed" );

    /* Set the socket to use TLS. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_REQUIRE_TLS,
                                  NULL,
                                  ( size_t ) 0 );

    /* Set the socket to use the secure server's root CA cert. */
    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE,
                                  tcptestECHO_HOST_ROOT_CA,
                                  sizeof( tcptestECHO_HOST_ROOT_CA ) );

    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SetSockOpt to require TLS failed" );

    /* Echo requests are sent to the secure echo server and port. */
    xAwsIotBrokerAddress.ulAddress = SOCKETS_GetHostByName( clientcredentialMQTT_BROKER_ENDPOINT );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( 0, xAwsIotBrokerAddress.ulAddress, "DNS look up failed." );
    xAwsIotBrokerAddress.usPort = SOCKETS_htons( clientcredentialMQTT_BROKER_PORT );

    xResult = SOCKETS_Connect( xSocket, &xAwsIotBrokerAddress, sizeof( xAwsIotBrokerAddress ) );
    TEST_ASSERT_LESS_THAN_INT32_MESSAGE( 0, xResult, "Connection permitted with untrusted server CA cert" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SockEventHandler )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTriggerWrongRootCA();
}

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

/** @brief This test will create a task the will send data to an echo server.
 * The data coming back will be received in that task.
 * The size of receiving buffer, the priority, the size of data send,
 * will keep changing to cover a maximum cases.
 */
static void prvSOCKETS_Threadsafe_SameSocketDifferentTasks( Server_t xConn )
{
    BaseType_t xTotalReceived, xReturned = 0;
    size_t xRecvLoop, xRecvLen;
    tcptestEchoTestModes_t xMode;
    BaseType_t xResult;
    volatile char * pcReceivedString;
    volatile BaseType_t xReceivedStringAllocated = pdFALSE;
    volatile BaseType_t xSocketPassingQueueAllocated = pdFALSE;
    volatile BaseType_t xSyncEventGroupAllocated = pdFALSE;
    volatile TaskHandle_t xCreatedTask;

    if( TEST_PROTECT() )
    {
        pcReceivedString = pvPortMalloc( ipconfigTCP_MSS * sizeof( char ) );
        configASSERT( pcReceivedString != NULL );
        xReceivedStringAllocated = pdTRUE;

        /* Create the queue used to pass the socket to use from the Tx task to the
         * Rx task. */
        xSocketPassingQueue = xQueueCreate( 1, sizeof( Socket_t ) );
        configASSERT( xSocketPassingQueue );
        xSocketPassingQueueAllocated = pdTRUE;

        /* Create the event group used by the Tx and Rx tasks to synchronize prior
         * to commencing a cycle using a new socket. */
        xSyncEventGroup = xEventGroupCreate();
        configASSERT( xSyncEventGroup );
        xSyncEventGroupAllocated = pdTRUE;

        /* Create the task that sends to an echo server, but lets a different task
         * receive the reply on the same socket. */
        xResult = xTaskCreate( prvEchoClientTxTask,              /* The function that implements the task. */
                               "EchoMultiTx",                    /* Just a text name for the task to aid debugging. */
                               tcptestTCP_ECHO_TASKS_STACK_SIZE, /* The stack size is defined in aws_demo_config.h. */
                               NULL,                             /* The task parameter, not used in this case. */
                               tcptestTCP_ECHO_TASKS_PRIORITY,   /* The priority assigned to the task is defined in aws_demo_config.h. */
                               ( TaskHandle_t * ) &xCreatedTask );
        TEST_ASSERT_EQUAL_MESSAGE( pdPASS, xResult, "Task creation failed" );

        for( xRecvLoop = 0; xRecvLoop < tcptestMAX_LOOPS_ECHO_TEST; xRecvLoop++ )
        {
            /* Attempt to establish the requested connection. */
            xResult = prvConnectHelperWithRetry( &xSocket, xConn, xEchoTestRxTxTimeOut, xEchoTestRxTxTimeOut, &xSocketOpen );
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

            /* Wait to receive the socket that will be used from the Tx task. */
            if( xEventGroupSync( xSyncEventGroup,                             /* The event group used for the rendezvous. */
                                 tcptestRX_TASK_BIT,                          /* The bit representing the Tx task reaching the rendezvous. */
                                 ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ), /* Also wait for the Rx task. */
                                 tcptestECHO_TEST_SYNC_TIMEOUT_TICKS ) != ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ) )
            {
                TEST_FAIL();
            }

            /* Nothing received yet. */
            xTotalReceived = 0;

            xMode = ( tcptestEchoTestModes_t ) ( xRecvLoop % tcptestMAX_ECHO_TEST_MODES ); /* % should be optimized to simple masking since only 4 modes are present.*/
                                                                                           /* Using % to avoid bug in case a new state is unknowingly added. */

            xRecvLen = ipconfigTCP_MSS;
            vTaskPrioritySet( NULL, tcptestECHO_TEST_HIGH_PRIORITY );

            /* Set low priority if requested . */
            if( ( xMode == LARGE_BUFFER_LOW_PRIORITY ) || ( xMode == SMALL_BUFFER_LOW_PRIORITY ) )
            {
                vTaskPrioritySet( NULL, tcptestECHO_TEST_LOW_PRIORITY );
            }

            if( ( xMode == SMALL_BUFFER_HIGH_PRIORITY ) || ( xMode == SMALL_BUFFER_LOW_PRIORITY ) )
            {
                xRecvLen = 1;
            }

            while( xTotalReceived < tcptestTWICE_MAX_FRAME_SIZE )
            {
                xReturned = SOCKETS_Recv( ( Socket_t ) xSocket, ( char * ) pcReceivedString, xRecvLen, 0 );

                TEST_ASSERT_NOT_EQUAL_MESSAGE( 0, xReturned, "Timeout occurred" );
                TEST_ASSERT_GREATER_THAN_MESSAGE( 0, xReturned, "Error occurred receiving large message" );

                /* Data was received. */
                TEST_ASSERT_EQUAL_MEMORY( &cTransmittedString[ xTotalReceived ], pcReceivedString, xReturned );

                xTotalReceived += xReturned;
            }

            /* Rendezvous with the Tx task ready to start a new cycle with a
             * different socket. */
            if( xEventGroupSync( xSyncEventGroup,                             /* The event group used for the rendezvous. */
                                 tcptestRX_TASK_BIT,                          /* The bit representing the Rx task reaching the rendezvous. */
                                 ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ), /* Also wait for the Tx task. */
                                 tcptestECHO_TEST_SYNC_TIMEOUT_TICKS ) != ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ) )
            {
                TEST_FAIL();
            }

            xResult = prvShutdownHelper( xSocket );
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

            xResult = prvCloseHelper( xSocket, &xSocketOpen );
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
        }
    }

    /* Free all dynamic memory. */
    if( xReceivedStringAllocated == pdTRUE )
    {
        vPortFree( ( char * ) pcReceivedString );
    }

    if( xSocketPassingQueueAllocated == pdTRUE )
    {
        vQueueDelete( xSocketPassingQueue );
    }

    if( xSyncEventGroupAllocated == pdTRUE )
    {
        vEventGroupDelete( xSyncEventGroup );
    }

    vTaskDelete( xCreatedTask );

    /* Set priority back. */
    vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
}

TEST( Full_TCP, AFQP_SOCKETS_Threadsafe_SameSocketDifferentTasks )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Threadsafe_SameSocketDifferentTasks( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Threadsafe_SameSocketDifferentTasks )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvSOCKETS_Threadsafe_SameSocketDifferentTasks( eSecure );
}

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

static void prvEchoClientTxTask( void * pvParameters )
{
    BaseType_t xReturned = 0;
    BaseType_t xTransmitted;
    BaseType_t xStatus;
    size_t xLenToSend, xSendLoop;
    size_t xMaxBufferSize;
    tcptestEchoTestModes_t xMode;


    /* Avoid warning about unused parameter. */
    ( void ) pvParameters;

    /* Offset by one so of sending and receiving task are not always the same. Sometimes both low/high or opposite. */

    /*  Recv task                   Sending task
     *  LARGE_BUFFER_HIGH_PRIORITY  SMALL_BUFFER_HIGH_PRIORITY
     *  SMALL_BUFFER_HIGH_PRIORITY  LARGE_BUFFER_LOW_PRIORITY
     *  LARGE_BUFFER_LOW_PRIORITY   SMALL_BUFFER_LOW_PRIORITY
     *  SMALL_BUFFER_LOW_PRIORITY   LARGE_BUFFER_HIGH_PRIORITY
     */
    for( xSendLoop = 1; xSendLoop < tcptestMAX_LOOPS_ECHO_TEST + 1; xSendLoop++ )
    {
        xMode = ( tcptestEchoTestModes_t ) ( xSendLoop % tcptestMAX_ECHO_TEST_MODES ); /* % should be optimized to simple masking since only 4 modes are present.*/
                                                                                       /* Using % to avoid bug in case a new state is unknowingly added. */

        vTaskPrioritySet( NULL, tcptestECHO_TEST_HIGH_PRIORITY );
        xMaxBufferSize = tcptestTWICE_MAX_FRAME_SIZE;

        /* Set low priority if requested . */
        if( ( xMode == LARGE_BUFFER_LOW_PRIORITY ) || ( xMode == SMALL_BUFFER_LOW_PRIORITY ) )
        {
            vTaskPrioritySet( NULL, tcptestECHO_TEST_LOW_PRIORITY );
        }

        /* Set buffer size to 1 if requested. */
        if( ( xMode == SMALL_BUFFER_HIGH_PRIORITY ) || ( xMode == SMALL_BUFFER_LOW_PRIORITY ) )
        {
            xMaxBufferSize = 1;
        }

        /* Wait for the Rx task to create and connect the socket. */
        if( xEventGroupSync( xSyncEventGroup,                             /* The event group used for the rendezvous. */
                             tcptestTX_TASK_BIT,                          /* The bit representing the Tx task reaching the rendezvous. */
                             ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ), /* Also wait for the Rx task. */
                             tcptestECHO_TEST_SYNC_TIMEOUT_TICKS ) != ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ) )
        {
            break;
        }

        xTransmitted = 0;
        xStatus = pdTRUE;

        /* Keep sending until the entire buffer has been sent. */
        while( xTransmitted < tcptestTWICE_MAX_FRAME_SIZE )
        {
            /* How many bytes are left to send?  Attempt to send them
             * all at once (so the length is potentially greater than the
             * MSS). */
            xLenToSend = tcptestTWICE_MAX_FRAME_SIZE - xTransmitted;

            /* Every loop switch the size of the packet from maximum to smallest. */
            if( xLenToSend > xMaxBufferSize )
            {
                xLenToSend = xMaxBufferSize;
            }

            xReturned = SOCKETS_Send( xSocket,                                            /* The socket being sent to. */
                                      ( void * ) &( cTransmittedString[ xTransmitted ] ), /* The data being sent. */
                                      xLenToSend,                                         /* The length of the data being sent. */
                                      0 );                                                /* ulFlags. */

            if( xReturned >= 0 )
            {
                /* Data was sent successfully. */
                xTransmitted += xReturned;
            }
            else
            {
                xStatus = pdFAIL;
                break;
            }
        }

        /* Wait for the Rx task to recognize the socket is closing and stop
         * using it. */

        if( xEventGroupSync( xSyncEventGroup,                             /* The event group used for the rendezvous. */
                             tcptestTX_TASK_BIT,                          /* The bit representing the Tx task reaching the rendezvous. */
                             ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ), /* Also wait for the Rx task. */
                             tcptestECHO_TEST_SYNC_TIMEOUT_TICKS ) != ( tcptestTX_TASK_BIT | tcptestRX_TASK_BIT ) )
        {
            xStatus = pdFAIL;
        }

        if( xStatus == pdFAIL )
        {
            break;
        }
    }

    vTaskSuspend( NULL ); /* Delete this task. */
}

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


/** @brief This test will create x tasks that will send and receive to an
 * echo server on different socket at the same time.
 */
void prvStartTCPEchoClientTasks_DifferentSockets( Server_t xConn )
{
    uint16_t usIndex;
    tcptestEchoClientsTaskParams_t xTcptestEchoClientsTaskParams[ tcptestNUM_ECHO_CLIENTS ];
    uint32_t ulEventMask;
    volatile BaseType_t xSyncEventGroupAllocated = pdFALSE;
    BaseType_t xResult;

    if( TEST_PROTECT() )
    {
        /* Create the event group used by the Tx and Rx tasks to synchronize prior
         * to commencing a cycle using a new socket. */
        xSyncEventGroup = xEventGroupCreate();
        configASSERT( xSyncEventGroup );
        xSyncEventGroupAllocated = pdTRUE;

        /* Create the echo client tasks. */
        for( usIndex = 0; usIndex < tcptestNUM_ECHO_CLIENTS; usIndex++ )
        {
            xTcptestEchoClientsTaskParams[ usIndex ].usTaskTag = usIndex;
            xTcptestEchoClientsTaskParams[ usIndex ].xConn = xConn;
            xTcptestEchoClientsTaskParams[ usIndex ].xResult = SOCKETS_SOCKET_ERROR;

            xResult = xTaskCreate( prvThreadSafeDifferentSocketsDifferentTasks,                 /* The function that implements the task. */
                                   "ClientTask",                                                /* Just a text name for the task to aid debugging. */
                                   tcptestTCP_ECHO_TASKS_STACK_SIZE,                            /* The stack size is defined in FreeRTOSIPConfig.h. */
                                   &( xTcptestEchoClientsTaskParams[ usIndex ] ),               /* The task parameter, not used in this case. */
                                   tcptestTCP_ECHO_TASKS_PRIORITY,                              /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
                                   &( xTcptestEchoClientsTaskParams[ usIndex ].xTaskHandle ) ); /* The task handle is not used. */
            TEST_ASSERT_EQUAL_MESSAGE( pdPASS, xResult, "Task creation failed" );
        }

        ulEventMask = xEventGroupSync( xSyncEventGroup, /* The event group used for the rendezvous. */
                                       0,
                                       tcptestECHO_CLIENT_EVENT_MASK,
                                       tcptestECHO_TEST_SYNC_TIMEOUT_TICKS );

        /* For each task not completed, delete the task.  */
        for( usIndex = 0; usIndex < tcptestNUM_ECHO_CLIENTS; usIndex++ )
        {
            if( ( ulEventMask & ( 1 << usIndex ) ) == 0 )
            {
                vTaskDelete( xTcptestEchoClientsTaskParams[ usIndex ].xTaskHandle );
            }
        }

        for( usIndex = 0; usIndex < tcptestNUM_ECHO_CLIENTS; usIndex++ )
        {
            TEST_ASSERT_EQUAL_MESSAGE( SOCKETS_ERROR_NONE,
                                       xTcptestEchoClientsTaskParams[ usIndex ].xResult,
                                       "Check iot_secure_sockets.h for error code" );
        }
    }

    if( xSyncEventGroupAllocated == pdTRUE )
    {
        vEventGroupDelete( xSyncEventGroup );
    }
}


TEST( Full_TCP, AFQP_SOCKETS_Threadsafe_DifferentSocketsDifferentTasks )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvStartTCPEchoClientTasks_DifferentSockets( eNonsecure );
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_Threadsafe_DifferentSocketsDifferentTasks )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvStartTCPEchoClientTasks_DifferentSockets( eSecure );
}
/*-----------------------------------------------------------*/

static void prvThreadSafeDifferentSocketsDifferentTasks( void * pvParameters )
{
    Socket_t xTaskSocket = SOCKETS_INVALID_SOCKET;
    int32_t lLoopCount = 0UL;
    BaseType_t xReceivedBytes, xReturned;
    BaseType_t xTransmitted;
    char * pcReceivedString;
    BaseType_t xResult = SOCKETS_SOCKET_ERROR;
    BaseType_t xReceivedStringAllocated = pdFALSE;
    BaseType_t xSocketOpen = pdFALSE;
    tcptestEchoClientsTaskParams_t * pxTcptestEchoClientsTaskParams;

    pxTcptestEchoClientsTaskParams = ( ( tcptestEchoClientsTaskParams_t * ) pvParameters );

    pcReceivedString = pvPortMalloc( ipconfigTCP_MSS * sizeof( char ) );

    if( pcReceivedString != NULL )
    {
        xReceivedStringAllocated = pdTRUE;
    }
    else
    {
        xResult = SOCKETS_SOCKET_ERROR;
        tcptestFAILUREPRINTF( ( "%s: Task %d failed to alloc memory \r\n",
                                __FUNCTION__,
                                ( int ) pxTcptestEchoClientsTaskParams->usTaskTag ) );
    }

    if( pcReceivedString != NULL )
    {
        /* Send a number of echo requests. */
        for( lLoopCount = 0; lLoopCount < tcptestMAX_LOOPS_ECHO_CLIENTS_LOOP; lLoopCount++ )
        {
            /* Attempt to establish the requested connection. */
            xResult = prvConnectHelperWithRetry( &xTaskSocket,
                                                 pxTcptestEchoClientsTaskParams->xConn,
                                                 xEchoTestRxTxTimeOut,
                                                 xEchoTestRxTxTimeOut,
                                                 &xSocketOpen );

            if( xResult != SOCKETS_ERROR_NONE )
            {
                tcptestFAILUREPRINTF( ( "%s: Task %d failed to connect with error code %d on loop %d \r\n",
                                        __FUNCTION__,
                                        xResult,
                                        lLoopCount,
                                        ( int ) pxTcptestEchoClientsTaskParams->usTaskTag ) );
                break;
            }

            /* Send the string to the socket. */
            xTransmitted = SOCKETS_Send( xTaskSocket,                   /* The socket being sent to. */
                                         ( void * ) cTransmittedString, /* The data being sent. */
                                         ipconfigTCP_MSS,               /* The length of the data being sent. */
                                         0 );                           /* No flags. */

            if( xTransmitted < ipconfigTCP_MSS )
            {
                tcptestFAILUREPRINTF( ( "%s: Task %d error  %ld while transmitting data\r\n",
                                        __FUNCTION__,
                                        ( int ) pxTcptestEchoClientsTaskParams->usTaskTag,
                                        xTransmitted ) );
                xResult = SOCKETS_SOCKET_ERROR;
                break;
            }

            /* Clear the buffer into which the echoed string will be
             * placed. */
            xReceivedBytes = 0;

            /* Receive data echoed back to the socket. */
            while( xReceivedBytes < xTransmitted )
            {
                xReturned = SOCKETS_Recv( xTaskSocket,                             /* The socket being received from. */
                                          &( pcReceivedString[ xReceivedBytes ] ), /* The buffer into which the received data will be written. */
                                          ipconfigTCP_MSS - xReceivedBytes,        /* The size of the buffer provided to receive the data. */
                                          0 );                                     /* No flags. */

                if( xReturned <= 0 )
                {
                    break;
                }

                xReceivedBytes += xReturned;
            }

            /* If an error occurred it will be latched in xReceivedBytes,
             * otherwise xReceived bytes will be just that - the number of
             * bytes received from the echo server. */
            if( xReceivedBytes == ipconfigTCP_MSS )
            {
                /* Compare the transmitted string to the received string. */
                if( strncmp( pcReceivedString, cTransmittedString, xTransmitted ) != 0 )
                {
                    tcptestFAILUREPRINTF( ( "%s: Task %d error while receiving data \r\n",
                                            __FUNCTION__,
                                            ( int ) pxTcptestEchoClientsTaskParams->usTaskTag ) );
                    xResult = SOCKETS_SOCKET_ERROR;
                    break;
                }
            }
            else
            {
                tcptestFAILUREPRINTF( ( "%s: Task %d error not enough bytes received \r\n",
                                        __FUNCTION__,
                                        ( int ) pxTcptestEchoClientsTaskParams->usTaskTag ) );
                xResult = SOCKETS_SOCKET_ERROR;
                break;
            }

            /* Close this socket before looping back to create another. */
            ( void ) prvShutdownHelper( xTaskSocket );
            ( void ) prvCloseHelper( xTaskSocket, &xSocketOpen );
        }
    }

    /* Free all dynamic memory. */
    if( xReceivedStringAllocated == pdTRUE )
    {
        vPortFree( pcReceivedString );
    }

    if( xSocketOpen == pdTRUE )
    {
        ( void ) prvCloseHelper( xTaskSocket, &xSocketOpen );
    }

    pxTcptestEchoClientsTaskParams->xResult = xResult;

    /* Don't wait, just flag it reached that point. */
    xEventGroupSync( xSyncEventGroup, /* The event group used for the rendezvous. */
                     ( 1 << pxTcptestEchoClientsTaskParams->usTaskTag ),
                     tcptestECHO_CLIENT_EVENT_MASK,
                     tcptestECHO_TEST_SYNC_TIMEOUT_TICKS );


    vTaskDelete( NULL ); /* Delete this task. */
}

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

static void prvNonBlockingConnect( void )
{
    BaseType_t xResult = pdFAIL;
    uint32_t ulDummy = 1;

    xSocket = prvTcpSocketHelper( &xSocketOpen );
    TEST_ASSERT_NOT_EQUAL_MESSAGE( SOCKETS_INVALID_SOCKET, xSocket, "Socket creation failed" );

    xResult = SOCKETS_SetSockOpt( xSocket,
                                  0,
                                  SOCKETS_SO_NONBLOCK,
                                  &ulDummy,
                                  sizeof( uint32_t ) );

    /* Nonblocking connect is not supported, setting the option before connecting should return an error. */
    TEST_ASSERT_LESS_THAN_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Port does not support nonblocking connect, but permitted sockopt to be set before connect. \r\n" );

    /* Test teardown will close the socket. */
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_NonBlockingConnect )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvNonBlockingConnect();
}

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

static void prvTwoSecureConnections( void )
{
    BaseType_t xResult = pdFAIL;
    uint8_t * pucRxBuffer = ( uint8_t * ) pcRxBuffer;
    uint32_t ulIndex;
    volatile Socket_t xSocketAWS;
    volatile BaseType_t xSocketOpenAWS = pdFALSE;
    volatile Socket_t xSocketSecServer;
    volatile BaseType_t xSocketOpenSecServer = pdFALSE;

    static const char cMessageAWS[] = "Message to AWS!  This message should not be sent to the secure server.";
    static const char cMessageSecServer[] = "Message to Secure Server!  This message should not be sent to AWS.";

    if( TEST_PROTECT() )
    {
        /* Create 2 sockets- one which will connect to AWS Broker, one which will connect to secure server. */

        xResult = prvConnectHelperWithRetry( &xSocketAWS, eAwsBroker, xReceiveTimeOut, xSendTimeOut, &xSocketOpenAWS );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect to AWS Broker" );

        xResult = prvConnectHelperWithRetry( &xSocketSecServer, eSecure, xReceiveTimeOut, xSendTimeOut, &xSocketOpenSecServer );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect to secure server" );

        /* Send message 1x to AWS Broker, 2x to Secure Echo Server, alternating. */
        xResult = prvSendHelper( xSocketSecServer, ( uint8_t * ) cMessageSecServer, sizeof( cMessageSecServer ) );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Send to secure server failed." );
        xResult = prvSendHelper( xSocketAWS, ( uint8_t * ) cMessageAWS, sizeof( cMessageAWS ) );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Send to AWS failed." );
        xResult = prvSendHelper( xSocketSecServer, ( uint8_t * ) cMessageSecServer, sizeof( cMessageSecServer ) );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Send to secure server failed." );

        /* Receive from secure echo server 1x. */
        xResult = prvRecvHelper( xSocketSecServer, pucRxBuffer, sizeof( cMessageSecServer ) );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Received incorrect number of bytes from sec server." );

        /* Verify the response is correct. */
        for( ulIndex = 0; ulIndex < sizeof( cMessageSecServer ); ulIndex++ )
        {
            if( pucRxBuffer[ ulIndex ] != cMessageSecServer[ ulIndex ] )
            {
                xResult = pdFAIL;
            }
        }

        TEST_ASSERT_EQUAL_MESSAGE( pdPASS, xResult, "Received incorrect message from secure server. " );

        /* There is no echo from the AWS broker, but let's make sure we don't see anything unexpected. */
        xResult = SOCKETS_Recv( xSocketAWS, pucRxBuffer, sizeof( cMessageAWS ), 0 /* flags. */ );

        if( ( xResult != 0 ) && ( xResult != SOCKETS_EWOULDBLOCK ) ) /* TS-2390 */
        {
            TEST_FAIL_MESSAGE( "Received bytes when there should be none to receive." );
        }

        /* Receive the second message from the secure echo server. */
        xResult = prvRecvHelper( xSocketSecServer, pucRxBuffer, sizeof( cMessageSecServer ) );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Received incorrect number of bytes from sec server." );

        for( ulIndex = 0; ulIndex < sizeof( cMessageSecServer ); ulIndex++ )
        {
            if( pucRxBuffer[ ulIndex ] != cMessageSecServer[ ulIndex ] )
            {
                xResult = pdFAIL;
            }
        }

        TEST_ASSERT_EQUAL_MESSAGE( pdPASS, xResult, "Received incorrect message from secure server. " );

        /* Shutdown both sockets. */
        xResult = prvShutdownHelper( xSocketAWS );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to shutdown socket." );
        xResult = prvShutdownHelper( xSocketSecServer );
        TEST_ASSERT_EQUAL_UINT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to shutdown socket." );
    }

    /* Close both sockets. */
    if( xSocketOpenAWS == pdTRUE )
    {
        if( TEST_PROTECT() )
        {
            xResult = prvCloseHelper( xSocketAWS, &xSocketOpenAWS );
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
        }
    }

    if( xSocketOpenSecServer == pdTRUE )
    {
        if( TEST_PROTECT() )
        {
            xResult = prvCloseHelper( xSocketSecServer, &xSocketOpenSecServer );
            TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
        }
    }
}

TEST( Full_TCP, AFQP_SECURE_SOCKETS_TwoSecureConnections )
{
    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    prvTwoSecureConnections();
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SOCKETS_htons_HappyCase )
{
    uint16_t usNetworkOrderValue;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    /* Convert the host order value to network order
     * value. */
    usNetworkOrderValue = SOCKETS_htons( 0x1234 );

    #if defined( socketsconfigBYTE_ORDER ) && ( socketsconfigBYTE_ORDER == pdLITTLE_ENDIAN )

        /* If the platform we are running on, is little
         * endian, bytes must have been swapped. */
        TEST_ASSERT_EQUAL_INT16_MESSAGE( 0x3412, usNetworkOrderValue, "SOCKETS_htons returned incorrect value." );
    #else

        /* If the platform we are running on, is big
         * endian, the output value must be same as
         * the input value. */
        TEST_ASSERT_EQUAL_INT16_MESSAGE( 0x1234, usNetworkOrderValue, "SOCKETS_htons returned incorrect value." );
    #endif

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SOCKETS_inet_addr_quick_HappyCase )
{
    uint32_t ulPackedIpAddress;

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    ulPackedIpAddress = SOCKETS_inet_addr_quick( 192, 168, 2, 6 );

    #if defined( socketsconfigBYTE_ORDER ) && ( socketsconfigBYTE_ORDER == pdLITTLE_ENDIAN )

        /* The expected return value of SOCKETS_inet_addr_quick
         * on a little endian platform must be same as the correct
         * hand calculated value. */
        TEST_ASSERT_EQUAL_INT32_MESSAGE( 0x0602A8C0, ulPackedIpAddress, "SOCKETS_inet_addr_quick returned incorrect value." );
    #else

        /* The expected return value of SOCKETS_inet_addr_quick
         * on a big endian platform must be same as the correct
         * hand calculated value. */
        TEST_ASSERT_EQUAL_INT32_MESSAGE( 0xC0A80206, ulPackedIpAddress, "SOCKETS_inet_addr_quick returned incorrect value." );
    #endif

    tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SECURE_SOCKETS_SetSecureOptionsAfterConnect )
{
    BaseType_t xResult = pdFAIL;
    char * pcAlpns[] = { socketsAWS_IOT_ALPN_MQTT };

    tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );

    if( TEST_PROTECT() )
    {
        xResult = prvConnectHelperWithRetry( &xSocket, eNonsecure, xReceiveTimeOut, xSendTimeOut, &xSocketOpen );
        TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Failed to connect" );

        /* Try to set the secure option AFTER the connect. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,
                                      SOCKETS_SO_REQUIRE_TLS,
                                      NULL,
                                      ( size_t ) 0 );
        TEST_ASSERT_LESS_THAN_MESSAGE( SOCKETS_ERROR_NONE, xResult, "TLS setup after connect succeed when fail was expected." );

        /* Try to set ALPN option after connect. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0, /* Level - Unused. */
                                      SOCKETS_SO_ALPN_PROTOCOLS,
                                      pcAlpns,
                                      sizeof( pcAlpns ) / sizeof( pcAlpns[ 0 ] ) );
        TEST_ASSERT_LESS_THAN_MESSAGE( SOCKETS_ERROR_NONE, xResult, "ALPN setup after connect succeed when fail was expected." );


        /* Try to set SNI option after connect. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0, /* Level - Unused. */
                                      SOCKETS_SO_SERVER_NAME_INDICATION,
                                      clientcredentialMQTT_BROKER_ENDPOINT,
                                      sizeof( clientcredentialMQTT_BROKER_ENDPOINT ) );
        TEST_ASSERT_LESS_THAN_MESSAGE( SOCKETS_ERROR_NONE, xResult, "SNI setup after connect succeed when fail was expected." );

        /* Try to set Trusted certificate option after connect. */
        xResult = SOCKETS_SetSockOpt( xSocket,
                                      0,
                                      SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE,
                                      tcptestECHO_HOST_ROOT_CA,
                                      sizeof( tcptestECHO_HOST_ROOT_CA ) );
        TEST_ASSERT_LESS_THAN_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Trusted server certificate setup after connect succeed when fail was expected." );
    }

    xResult = prvShutdownHelper( xSocket );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to shutdown" );

    xResult = prvCloseHelper( xSocket, &xSocketOpen );
    TEST_ASSERT_EQUAL_INT32_MESSAGE( SOCKETS_ERROR_NONE, xResult, "Socket failed to close" );
}

/* TODO: Investigate tests for loopback, other reserved IP addresses */
/* TODO: Implement tests with a bad TCP connection (dropped packets, repeated packets, connection refused etc */
/* TODO: Implement tests that have memory allocation errors (freertos heap is full) */
/*-------------------------------------------------------------------*/
/*-----------------------End Tests-----------------------------------*/
/*-------------------------------------------------------------------*/
