/*
 * FreeRTOS Common IO V0.1.3
 * Copyright (C) 2021 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
 */

/*******************************************************************************
 * @file iot_test_flash.c
 * @brief Functional Unit Test - FLASH
 *******************************************************************************
 */

#include "unity.h"
#include "unity_fixture.h"
#include "iot_flash.h"
#include "iot_timer.h"

#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"

/* Default byte value in flash. */
#define testIotFLASH_DEFAULT_FLASH_BYTE           ( 0xFF )
/* Assuming that pagesize is greater than 32B */
#define testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE    ( 0x20 )
/* Use 512 bytes max */
#define testIotFLASH_DEFAULT_MAX_BUFFER_SIZE      ( 0x200 )

#define testIotFLASH_DEFAULT_DELAY_US             ( 100 )
#define testIotFLASH_INVALID_HANDLE               ( -1 )

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

/* Globals values which can be overwritten by the test
 * framework invoking these tests */
/*-----------------------------------------------------------*/
extern int32_t ltestIotTimerInstance;

uint8_t ltestIotFlashInstance = 0; /** Default Flash instance for testing */

/* This offset is used for testing the flash test-cases
 * The data from offset to offset + 2 sectors will be corrupted */
uint32_t ultestIotFlashStartOffset = 0;

/*-----------------------------------------------------------*/
/** Static globals */
/*-----------------------------------------------------------*/
static uint8_t uctestIotFlashWriteBuffer[ testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ];
static uint8_t uctestIotFlashReadBuffer[ testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ];

static SemaphoreHandle_t xtestIotFlashSemaphore = NULL;
static SemaphoreHandle_t xtestIotFlashTimerSemaphore = NULL;

static const uint8_t uctestIotFlashWriteROBuffer[ 512 ] = { 0xAA };

/* Define Test Group. */
TEST_GROUP( TEST_IOT_FLASH );

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

/**
 * @brief Setup function called before each test in this group is executed.
 */
TEST_SETUP( TEST_IOT_FLASH )
{
}

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

/**
 * @brief Tear down function called after each test in this group is executed.
 */
TEST_TEAR_DOWN( TEST_IOT_FLASH )
{
}

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

/*-----------------------------------------------------------*/
static void prvIotFlashEraseCallback( IotFlashOperationStatus_t pxStatus,
                                      void * pvUserContext )
{
    BaseType_t xHigherPriorityTaskWoken;

    xSemaphoreGiveFromISR( xtestIotFlashSemaphore, &xHigherPriorityTaskWoken );
}

/*-----------------------------------------------------------*/
static void prvIotTimerCallback( void * pvUserContext )
{
    int32_t lRetVal;

    BaseType_t xHigherPriorityTaskWoken;

    /* Suspend the flash write/erase */
    lRetVal = iot_flash_ioctl( ( IotFlashHandle_t ) pvUserContext,
                               eSuspendFlashProgramErase,
                               NULL );

    /* Check if suspend/resume is supported */
    if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
    {
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
    }

    xSemaphoreGiveFromISR( xtestIotFlashTimerSemaphore, &xHigherPriorityTaskWoken );
}

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

static void prvIotFlashWriteDummyData( IotFlashHandle_t xFlashHandle,
                                       uint32_t offset,
                                       uint32_t size )
{
    uint32_t lRetVal;
    uint32_t i;
    uint32_t j;

    /* Fill out a buffer of pageSize to be written */
    for( i = 0; i < size; i += testIotFLASH_DEFAULT_MAX_BUFFER_SIZE )
    {
        /* Less the a full buffer left? */
        uint32_t remaining_size = ( ( i + testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ) > size ) ? ( size - i ) : testIotFLASH_DEFAULT_MAX_BUFFER_SIZE;

        if( remaining_size > 0 )
        {
            for( j = 0; j < remaining_size; j++ )
            {
                uctestIotFlashWriteBuffer[ j ] = j;
            }

            /* Write full page of data */
            lRetVal = iot_flash_write_sync( xFlashHandle,
                                            offset + i,
                                            &uctestIotFlashWriteBuffer[ 0 ],
                                            remaining_size );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        }
    }
}

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

static void prvIotFlashReadVerifyDummyData( IotFlashHandle_t xFlashHandle,
                                            uint32_t offset,
                                            uint32_t size )
{
    uint32_t lRetVal;
    uint32_t i;
    uint32_t j;

    for( i = 0; i < size; i += testIotFLASH_DEFAULT_MAX_BUFFER_SIZE )
    {
        /* Less the a full buffer left? */
        uint32_t remaining_size = ( ( i + testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ) > size ) ? ( size - i ) : testIotFLASH_DEFAULT_MAX_BUFFER_SIZE;

        if( remaining_size > 0 )
        {
            /* Read back the data written */
            lRetVal = iot_flash_read_sync( xFlashHandle,
                                           offset + i,
                                           &uctestIotFlashReadBuffer[ 0 ],
                                           remaining_size );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Verify that  the data was written. */
            for( j = 0; j < remaining_size; j++ )
            {
                if( uctestIotFlashReadBuffer[ j ] != ( j & 0xFF ) )
                {
                    TEST_ASSERT_MESSAGE( 0, "Data was NOT written" );
                }
            }
        }
    }
}

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

static void prvIotFlashReadVerifyErased( IotFlashHandle_t xFlashHandle,
                                         uint32_t offset,
                                         uint32_t size )
{
    uint32_t lRetVal;
    uint32_t i;
    uint32_t j;

    for( i = 0; i < size; i += testIotFLASH_DEFAULT_MAX_BUFFER_SIZE )
    {
        /* Less the a full buffer left? */
        uint32_t remaining_size = ( ( i + testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ) > size ) ? ( size - i ) : testIotFLASH_DEFAULT_MAX_BUFFER_SIZE;

        if( remaining_size > 0 )
        {
            /* Read the data */
            lRetVal = iot_flash_read_sync( xFlashHandle,
                                           offset + i,
                                           &uctestIotFlashReadBuffer[ 0 ],
                                           remaining_size );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Verify that the data was erased. */
            for( j = 0; j < remaining_size; j++ )
            {
                if( uctestIotFlashReadBuffer[ j ] != testIotFLASH_DEFAULT_FLASH_BYTE )
                {
                    TEST_ASSERT_MESSAGE( 0, "Data was NOT erased" );
                }
            }
        }
    }
}

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

static void prvIotFlashWriteReadVerify( IotFlashHandle_t xFlashHandle,
                                        uint32_t offset,
                                        uint32_t size )
{
    /* Fill out a buffer of pageSize to be written */
    prvIotFlashWriteDummyData( xFlashHandle, offset, size );

    /* Read back the data written */
    prvIotFlashReadVerifyDummyData( xFlashHandle, offset, size );
}

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

/**
 * @brief Function to define which tests to execute as part of this group.
 */
TEST_GROUP_RUNNER( TEST_IOT_FLASH )
{
    xtestIotFlashSemaphore = xSemaphoreCreateBinary();
    TEST_ASSERT_NOT_EQUAL( NULL, xtestIotFlashSemaphore );

    xtestIotFlashTimerSemaphore = xSemaphoreCreateBinary();
    TEST_ASSERT_NOT_EQUAL( NULL, xtestIotFlashTimerSemaphore );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashOpenClose );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashReadInfo );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseSector );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseMultipleSectors );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocks );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocksUnAlignedAddress );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocksUnAlignedSize );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseUnAlignedAddress );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseUnAlignedSize );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWritePartialPage );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWritePage );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteSector );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteAcrossSectors );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseInProgressRead );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseInProgressWrite );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteProtectWriteFailure );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteProtectEraseFailure );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteEraseReadCycle );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWritePageFromFlash );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashOpenCloseFuzz );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashGetInfoFuzz );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashIoctlFuzz );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteFuzz );
    RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashReadFuzz );

    /* TODO - These tests require suspend/resume functionality to be enabled
     * which requires int handlers to be moved to RAM
     */
    #if 0
        RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashWriteSuspendResume );
        RUN_TEST_CASE( TEST_IOT_FLASH, AFQP_IotFlashEraseSuspendResume );
    #endif
}

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

/**
 * @brief Test Function to test iot_flash_open and iot_flash_close.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashOpenClose )
{
    IotFlashHandle_t xFlashHandle;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test iot_flash_open, read the flash_info
 * validate the flash_info and iot_flash_close.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashReadInfo )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /*
         * Check the flash size, block size, sector size and page size
         */
        TEST_ASSERT_NOT_EQUAL( 0, pxFlashInfo->ulPageSize );
        TEST_ASSERT_GREATER_OR_EQUAL( pxFlashInfo->ulPageSize, pxFlashInfo->ulSectorSize );
        TEST_ASSERT_GREATER_OR_EQUAL( pxFlashInfo->ulSectorSize, pxFlashInfo->ulBlockSize );
        TEST_ASSERT_GREATER_OR_EQUAL( pxFlashInfo->ulBlockSize, pxFlashInfo->ulFlashSize );

        /* Make sure the flash size, block size, sector size and page size are the power of 2 */
        TEST_ASSERT_EQUAL( 0, pxFlashInfo->ulPageSize & ( pxFlashInfo->ulPageSize - 1 ) );
        TEST_ASSERT_EQUAL( 0, pxFlashInfo->ulSectorSize & ( pxFlashInfo->ulSectorSize - 1 ) );
        TEST_ASSERT_EQUAL( 0, pxFlashInfo->ulBlockSize & ( pxFlashInfo->ulBlockSize - 1 ) );
        TEST_ASSERT_EQUAL( 0, pxFlashInfo->ulFlashSize & ( pxFlashInfo->ulFlashSize - 1 ) );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}


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

/**
 * @brief Test Function to test one sector erase and verify the contents are erased.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseSector )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the sector before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Fill a sector with dummy data and verify it */
        prvIotFlashWriteReadVerify( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize );

        /* Erase one sector */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Read back the whole sector to make sure the contents are erased.
         */
        prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test erasing multiple sectors and verify the contents are erased.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseMultipleSectors )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase 2 sectors */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize * 2 );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Read back the two sectors to make sure the contents are erased.
         */
        prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize * 2 );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

/**
 * @brief Test Function to test erasing various size blocks (32K, 64K)
 * and make sure erase is being done correctly.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocks )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulMiddleOffset;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Make sure the offset is aligned to BlockSize */
        if( ( ultestIotFlashStartOffset & ( pxFlashInfo->ulBlockSize - 1 ) ) == 0 )
        {
            /* If Erase async is supported, register a callback */
            if( pxFlashInfo->ucAsyncSupported )
            {
                iot_flash_set_callback( xFlashHandle,
                                        prvIotFlashEraseCallback,
                                        NULL );
            }

            /* Erase the 2 sectors before writing to it. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize * 2 );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            ulMiddleOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulSectorSize / 2 ) );

            /*
             * Write dummy data starting at the middle of the sector to half of next sector
             * and read it back to verify the data.
             */
            prvIotFlashWriteReadVerify( xFlashHandle, ulMiddleOffset, pxFlashInfo->ulSectorSize );

            /* Erase a Block */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulBlockSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            /* Read back the block to make sure the contents are erased.
             */
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulBlockSize );
        }
        else
        {
            /* TEST_MESSAGE( "Start offset is not aligned with blockSize" ); */
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

/**
 * @brief Test Function to test erasing blockSize minus sector Size when address is
 * one sector greater than block alignment, and make sure the sector not included in
 * the Address is not erased, but the rest is erased.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocksUnAlignedAddress )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulMiddleOffset;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Make sure the offset is aligned to BlockSize */
        if( ( ultestIotFlashStartOffset & ( pxFlashInfo->ulBlockSize - 1 ) ) == 0 )
        {
            /* If Erase async is supported, register a callback */
            if( pxFlashInfo->ucAsyncSupported )
            {
                iot_flash_set_callback( xFlashHandle,
                                        prvIotFlashEraseCallback,
                                        NULL );
            }

            /* Erase the 2 sectors before writing to it. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize * 2 );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            ulMiddleOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulSectorSize / 2 ) );

            /*
             * Write dummy data starting at the middle of the sector to half of next sector
             * and read it back to verify the data.
             */
            prvIotFlashWriteReadVerify( xFlashHandle, ulMiddleOffset, pxFlashInfo->ulSectorSize );

            /* Erase a Block minus sector size starting at block boundary plus sector size */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset + pxFlashInfo->ulSectorSize,
                                               pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            /* Read back the block to make sure the contents are erased.
             */
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset + pxFlashInfo->ulSectorSize,
                                         pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize );

            /* Read and make sure that the first sector written is not erased */
            ulMiddleOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulSectorSize / 2 ) );

            prvIotFlashReadVerifyDummyData( xFlashHandle, ulMiddleOffset, pxFlashInfo->ulSectorSize / 2 );
        }
        else
        {
            /* TEST_MESSAGE( "Start offset is not aligned with blockSize" ); */
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

/**
 * @brief Test Function to test erasing blockSize minus sector Size when size is
 * one sector less than block alignment, and make sure the sector not included in
 * the block size is not erased, but the rest is erased.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseFlashBlocksUnAlignedSize )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulOffset;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Make sure the offset is aligned to BlockSize */
        if( ( ultestIotFlashStartOffset & ( pxFlashInfo->ulBlockSize - 1 ) ) == 0 )
        {
            /* If Erase async is supported, register a callback */
            if( pxFlashInfo->ucAsyncSupported )
            {
                iot_flash_set_callback( xFlashHandle,
                                        prvIotFlashEraseCallback,
                                        NULL );
            }

            /* Erase the 2 sectors before writing to it. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize * 2 );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            ulOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulSectorSize / 2 ) );

            /*
             * Write dummy data starting at the middle of the sector to half of next sector
             * and read it back to verify the data.
             */
            prvIotFlashWriteReadVerify( xFlashHandle, ulOffset, pxFlashInfo->ulSectorSize );

            /* Also write the last sector in the block to make sure its not erased */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset + ( pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize ),
                                               pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            ulOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize ) );

            prvIotFlashWriteReadVerify( xFlashHandle, ulOffset, pxFlashInfo->ulSectorSize );

            /* Erase a Block minus sector size starting at block boundary */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            /* Read back the block to make sure the contents are erased.
             */
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset,
                                         pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize );

            /* Read and make sure that the last sector written is not erased */
            ulOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulBlockSize - pxFlashInfo->ulSectorSize ) );
            prvIotFlashReadVerifyDummyData( xFlashHandle, ulOffset, pxFlashInfo->ulSectorSize );
        }
        else
        {
            /* TEST_MESSAGE( "Start offset is not aligned with blockSize" ); */
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test erase return an error when un-aligned address is
 * passed to erase function
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseUnAlignedAddress )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Try erasing sector where offset is not aligned. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset + 1,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test erase return an error when un-aligned size is
 * passed to erase function
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseUnAlignedSize )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Try erasing sector where offset is not aligned. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize + 1 );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to write to a page with multiple writes,
 * and make sure that the read contents are correct.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWritePartialPage )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    int8_t i;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the sector before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* fill out a buffer (Partial page) to be written */
        for( i = 0; i < testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE * 2; i++ )
        {
            uctestIotFlashWriteBuffer[ i ] = i;
        }

        /* Write partial page (< page size) */
        lRetVal = iot_flash_write_sync( xFlashHandle,
                                        ultestIotFlashStartOffset,
                                        &uctestIotFlashWriteBuffer[ 0 ],
                                        testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Write partial page starting from the end of previous
         * write and make sure the second write does not overwrite the first
         */
        lRetVal = iot_flash_write_sync( xFlashHandle,
                                        ultestIotFlashStartOffset + testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE,
                                        &uctestIotFlashWriteBuffer[ testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE ],
                                        testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Read back the data written */
        lRetVal = iot_flash_read_sync( xFlashHandle,
                                       ultestIotFlashStartOffset,
                                       &uctestIotFlashReadBuffer[ 0 ],
                                       testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE * 2 );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Verify the data written. */
        for( i = 0; i < testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE * 2; i++ )
        {
            if( uctestIotFlashReadBuffer[ i ] != uctestIotFlashWriteBuffer[ i ] )
            {
                TEST_ASSERT_MESSAGE( 0, "Contents do NOT match when readback" );
            }
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test writing a page to flash and verify contents.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWritePage )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the sector before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Write the a full page and verify the data by reading it back */
        prvIotFlashWriteReadVerify( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test writing greater than page but less than or equal to sector
 * to flash and verify contents.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteSector )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the sector before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Write the a full sector and verify the data by reading it back */
        prvIotFlashWriteReadVerify( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test across 2 sectors and verify the data
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteAcrossSectors )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulOffset;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the 2 sectors before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize * 2 );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        ulOffset = ( ultestIotFlashStartOffset + ( pxFlashInfo->ulSectorSize / 2 ) );

        /*
         * Try to write dummy data across twi sectors and read it back to verify the data.
         */
        prvIotFlashWriteReadVerify( xFlashHandle, ulOffset, pxFlashInfo->ulSectorSize );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test Read when erase in progress.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseInProgressRead )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulSize;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Erase multiple sectors to get some time for read while erase. */
        ulSize = pxFlashInfo->ulSectorSize * 2;
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           ulSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Try reading the data from the same sector(s) being erased. */
        lRetVal = iot_flash_read_sync( xFlashHandle,
                                       ultestIotFlashStartOffset,
                                       &uctestIotFlashReadBuffer[ 0 ],
                                       testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );

        if( lRetVal != IOT_FLASH_DEVICE_BUSY )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test Write when Flash Erase is in progress.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseInProgressWrite )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t ulSize;
    int8_t i;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Erase multiple sectors to get some time for read while erase. */
        ulSize = pxFlashInfo->ulSectorSize * 2;
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           ulSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Fill out a buffer of sectorSize to be written */
        for( i = 0; i < testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE; i++ )
        {
            uctestIotFlashWriteBuffer[ i ] = i;
        }

        /* Try writing the the same sector when erase is in progress. */
        lRetVal = iot_flash_write_sync( xFlashHandle,
                                        ultestIotFlashStartOffset,
                                        &uctestIotFlashWriteBuffer[ 0 ],
                                        testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );

        if( lRetVal != IOT_FLASH_DEVICE_BUSY )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
            /* Read back the data written */
            lRetVal = iot_flash_read_sync( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           &uctestIotFlashReadBuffer[ 0 ],
                                           testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Verify the data written. */
            for( i = 0; i < testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE; i++ )
            {
                if( uctestIotFlashReadBuffer[ i ] != uctestIotFlashWriteBuffer[ i ] )
                {
                    TEST_ASSERT_MESSAGE( 0, "Contents do NOT match when readback" );
                }
            }
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test Write failure when write protection is enabled.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteProtectWriteFailure )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    IotFlashWriteProtectConfig_t xFlashProtect;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase a sector before trying to write to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Make the sector as read only. */
        xFlashProtect.ulAddress = ultestIotFlashStartOffset;
        xFlashProtect.ulSize = pxFlashInfo->ulLockSupportSize;
        xFlashProtect.xProtectionLevel = eFlashReadOnly;

        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eSetFlashBlockProtection,
                                   ( void * const ) &xFlashProtect );

        /* Check if block protection is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Read back the block protection */
            lRetVal = iot_flash_ioctl( xFlashHandle,
                                       eGetFlashBlockProtection,
                                       ( void * const ) &xFlashProtect );

            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Write the a full page and verify the data is not written reading it back */
            prvIotFlashWriteDummyData( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
        }

        /* Now make the sector writeable */
        xFlashProtect.ulAddress = ultestIotFlashStartOffset;
        xFlashProtect.ulSize = pxFlashInfo->ulLockSupportSize;
        xFlashProtect.xProtectionLevel = eFlashReadWrite;
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eSetFlashBlockProtection,
                                   ( void * const ) &xFlashProtect );

        /* Check if block protection is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Read back the block protection */
            lRetVal = iot_flash_ioctl( xFlashHandle,
                                       eGetFlashBlockProtection,
                                       ( void * const ) &xFlashProtect );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Write the a full page and verify the data by reading it back */
            prvIotFlashWriteReadVerify( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test Erase failure when write protect is enabled.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteProtectEraseFailure )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    IotFlashWriteProtectConfig_t xFlashProtect;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase a sector before trying to write to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Verify that the page was erased */
        prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );

        /* Fill out a buffer of sectorSize to be written */
        prvIotFlashWriteDummyData( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );

        /* Make the sector as read only. */
        xFlashProtect.ulAddress = ultestIotFlashStartOffset;
        xFlashProtect.ulSize = pxFlashInfo->ulLockSupportSize;
        xFlashProtect.xProtectionLevel = eFlashReadOnly;
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eSetFlashBlockProtection,
                                   ( void * const ) &xFlashProtect );

        /* Check if block protection is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Read back the block protection */
            lRetVal = iot_flash_ioctl( xFlashHandle,
                                       eGetFlashBlockProtection,
                                       ( void * const ) &xFlashProtect );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Try erasing the sector. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            /* Read back the data written */
            prvIotFlashReadVerifyDummyData( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
        }

        /* Now make the sector as writeable. */
        xFlashProtect.ulAddress = ultestIotFlashStartOffset;
        xFlashProtect.ulSize = pxFlashInfo->ulLockSupportSize;
        xFlashProtect.xProtectionLevel = eFlashReadWrite;
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eSetFlashBlockProtection,
                                   ( void * const ) &xFlashProtect );

        /* Check if block protection is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Read back the block protection */
            lRetVal = iot_flash_ioctl( xFlashHandle,
                                       eGetFlashBlockProtection,
                                       ( void * const ) &xFlashProtect );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Try erasing the sector. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            if( pxFlashInfo->ucAsyncSupported )
            {
                /* Wait for the Erase to be completed and callback is called. */
                lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
                TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
            }

            /* Read back the data written and verify that it is erased */
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}


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

/**
 * @brief Test Function to test Erase-Read-Write-Read-Erase-Read cycle
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteEraseReadCycle )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t idx;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Erase a sector before trying to write to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        /* Read back the data and make sure that data is erased first */
        prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );

        for( idx = 0; idx < 5; idx++ )
        {
            /* Write to this sector and veirfy */
            prvIotFlashWriteReadVerify( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );

            /* Get the flash information. */
            pxFlashInfo = iot_flash_getinfo( xFlashHandle );
            TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

            /* Erase the sector. */
            lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                               ultestIotFlashStartOffset,
                                               pxFlashInfo->ulSectorSize );
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

            /* Read back the data and make sure that data is erased */
            prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulPageSize );
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}


/**
 * @brief Test Function to test Write suspend and resume operations
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteSuspendResume )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    IotTimerHandle_t xTimerHandle;
    int32_t lRetVal;
    uint32_t lStatus;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Setup a timer to suspend the write */
        xTimerHandle = iot_timer_open( ltestIotTimerInstance );
        TEST_ASSERT_NOT_EQUAL( NULL, xTimerHandle );

        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the 2 sectors before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize * 2 );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Set up the timer callback */
        iot_timer_set_callback( xTimerHandle,
                                prvIotTimerCallback,
                                xFlashHandle );

        /* Set up the timer delay */
        lRetVal = iot_timer_delay( xTimerHandle,
                                   testIotFLASH_DEFAULT_DELAY_US );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );

        /* Start the timer */
        lRetVal = iot_timer_start( xTimerHandle );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );

        /* Fill out a sector with data */
        prvIotFlashWriteDummyData( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize );

        /* Wait for the Delay callback to be called. */
        lRetVal = xSemaphoreTake( xtestIotFlashTimerSemaphore, portMAX_DELAY );
        TEST_ASSERT_EQUAL( pdTRUE, lRetVal );

        /* Verify that current write is suspended */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eGetFlashStatus,
                                   ( void * const ) &lStatus );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        TEST_ASSERT_EQUAL( eFlashProgramSuspended, lStatus );

        /* Resume the current write operation */
        /* Suspend the flash write/erase */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eResumeFlashProgramErase,
                                   NULL );

        /* Check if suspend/resume is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        }

        /* Verify that current write is resumed */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eGetFlashStatus,
                                   ( void * const ) &lStatus );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        TEST_ASSERT_EQUAL( eFlashIdle, lStatus );

        /* Read the data back to make sure Resume succeeded and data is written */
        prvIotFlashReadVerifyDummyData( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize );

        lRetVal = iot_timer_close( xTimerHandle );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

/**
 * @brief Test Function to test Erase suspend and resume operations
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseSuspendResume )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    IotTimerHandle_t xTimerHandle;
    int32_t lRetVal;
    uint32_t lStatus;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Setup a timer to suspend the write */
        xTimerHandle = iot_timer_open( ltestIotTimerInstance );
        TEST_ASSERT_NOT_EQUAL( NULL, xTimerHandle );

        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* Set up the timer callback */
        iot_timer_set_callback( xTimerHandle,
                                prvIotTimerCallback,
                                xFlashHandle );

        /* Set up the timer delay */
        lRetVal = iot_timer_delay( xTimerHandle,
                                   testIotFLASH_DEFAULT_DELAY_US );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );

        /* Start the timer */
        lRetVal = iot_timer_start( xTimerHandle );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the 2 sectors before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize * 2 );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );


        /* Wait for the Delay callback to be called. */
        lRetVal = xSemaphoreTake( xtestIotFlashTimerSemaphore, portMAX_DELAY );
        TEST_ASSERT_EQUAL( pdTRUE, lRetVal );

        /* Verify that current erase is suspended */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eGetFlashStatus,
                                   ( void * const ) &lStatus );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        TEST_ASSERT_EQUAL( eFlashEraseSuspended, lStatus );

        /* Resume the current erase operation */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eResumeFlashProgramErase,
                                   NULL );

        /* Check if suspend/resume is supported */
        if( lRetVal != IOT_FLASH_FUNCTION_NOT_SUPPORTED )
        {
            TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        }

        /* Verify that current erase is resumed */
        lRetVal = iot_flash_ioctl( xFlashHandle,
                                   eGetFlashStatus,
                                   ( void * const ) &lStatus );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
        TEST_ASSERT_EQUAL( eFlashIdle, lStatus );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Read the data back to make sure Resume succeeded and data is erased */
        prvIotFlashReadVerifyErased( xFlashHandle, ultestIotFlashStartOffset, pxFlashInfo->ulSectorSize * 2 );

        lRetVal = iot_timer_close( xTimerHandle );
        TEST_ASSERT_EQUAL( IOT_TIMER_SUCCESS, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test writing a page to flash and verify contents.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWritePageFromFlash )
{
    IotFlashHandle_t xFlashHandle;
    IotFlashInfo_t * pxFlashInfo;
    int32_t lRetVal;
    uint32_t i;
    uint32_t j;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Get the flash information. */
        pxFlashInfo = iot_flash_getinfo( xFlashHandle );
        TEST_ASSERT_NOT_EQUAL( NULL, pxFlashInfo );

        /* If Erase async is supported, register a callback */
        if( pxFlashInfo->ucAsyncSupported )
        {
            iot_flash_set_callback( xFlashHandle,
                                    prvIotFlashEraseCallback,
                                    NULL );
        }

        /* Erase the sector before writing to it. */
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           pxFlashInfo->ulSectorSize );
        TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

        if( pxFlashInfo->ucAsyncSupported )
        {
            /* Wait for the Erase to be completed and callback is called. */
            lRetVal = xSemaphoreTake( xtestIotFlashSemaphore, portMAX_DELAY );
            TEST_ASSERT_EQUAL( pdTRUE, lRetVal );
        }

        /* Write full page of data */
        for( i = 0; i < pxFlashInfo->ulPageSize; i += testIotFLASH_DEFAULT_MAX_BUFFER_SIZE )
        {
            /* Less the a full buffer left? */
            uint32_t remaining_size = ( ( i + testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ) > pxFlashInfo->ulPageSize ) ? ( pxFlashInfo->ulPageSize - i ) : testIotFLASH_DEFAULT_MAX_BUFFER_SIZE;

            if( remaining_size > 0 )
            {
                for( j = 0; j < remaining_size; j++ )
                {
                    uctestIotFlashWriteBuffer[ j ] = j;
                }

                /* Write full page of data */
                lRetVal = iot_flash_write_sync( xFlashHandle,
                                                ultestIotFlashStartOffset + i,
                                                uctestIotFlashWriteROBuffer,
                                                remaining_size );
                TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
            }
        }

        for( i = 0; i < pxFlashInfo->ulPageSize; i += testIotFLASH_DEFAULT_MAX_BUFFER_SIZE )
        {
            /* Less the a full buffer left? */
            uint32_t remaining_size = ( ( i + testIotFLASH_DEFAULT_MAX_BUFFER_SIZE ) > pxFlashInfo->ulPageSize ) ? ( pxFlashInfo->ulPageSize - i ) : testIotFLASH_DEFAULT_MAX_BUFFER_SIZE;

            if( remaining_size > 0 )
            {
                /* Read back the data written */
                lRetVal = iot_flash_read_sync( xFlashHandle,
                                               ultestIotFlashStartOffset + i,
                                               &uctestIotFlashReadBuffer[ 0 ],
                                               remaining_size );
                TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

                /* Verify the data is erased. */
                for( i = 0; i < remaining_size; i++ )
                {
                    if( uctestIotFlashReadBuffer[ i ] != uctestIotFlashWriteROBuffer[ i ] )
                    {
                        TEST_ASSERT_MESSAGE( 0, "Data was NOT erased" );
                    }
                }
            }
        }
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}
/*-----------------------------------------------------------*/

/**
 * @brief Test Function to test fuzzing of iot_flash_open and iot_flash_close.
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashOpenCloseFuzz )
{
    IotFlashHandle_t xFlashHandle, xFlashHandleTmp;
    int32_t lRetVal;

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* open the same instance twice */
        xFlashHandleTmp = iot_flash_open( ltestIotFlashInstance );
        TEST_ASSERT_EQUAL( NULL, xFlashHandleTmp );

        /* Open flash to initialize hardware with invalid handle. */
        xFlashHandleTmp = iot_flash_open( testIotFLASH_INVALID_HANDLE );
        TEST_ASSERT_EQUAL( NULL, xFlashHandleTmp );
    }

    /* Close with valid handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );

    /* Close with stale handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
}

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

/**
 * @brief Test Function to test fuzzing of iot_flash_getinfo
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashGetInfoFuzz )
{
    IotFlashInfo_t * pxFlashInfo;

    /* Close with stale handle */
    pxFlashInfo = iot_flash_getinfo( NULL );
    TEST_ASSERT_EQUAL( NULL, pxFlashInfo );
}

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

/**
 * @brief Test Function to test fuzzing of iot_flash_ioctl
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashIoctlFuzz )
{
    IotFlashHandle_t xFlashHandle;
    int32_t lRetVal;
    uint32_t lStatus;

    /* Call ioctl with null handle */
    lRetVal = iot_flash_ioctl( NULL, eGetFlashStatus, ( void * const ) &lStatus );
    TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Call ioctl with handle, but invalid enum */
        lRetVal = iot_flash_ioctl( xFlashHandle, -1, NULL );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test fuzzing of iot_flash_erase
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashEraseFuzz )
{
    IotFlashHandle_t xFlashHandle;
    int32_t lRetVal;

    /* Erase one sector with NULL handle*/
    lRetVal = iot_flash_erase_sectors( NULL,
                                       ultestIotFlashStartOffset,
                                       testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
    TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Erase one sector with invalid start address*/
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           -1,
                                           testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

        /* Erase one sector with invalid length*/
        lRetVal = iot_flash_erase_sectors( xFlashHandle,
                                           ultestIotFlashStartOffset,
                                           -1 );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test fuzzing of iot_flash_write
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashWriteFuzz )
{
    IotFlashHandle_t xFlashHandle;
    int32_t lRetVal;

    /* Write partial page with invalid handle */
    lRetVal = iot_flash_write_sync( NULL,
                                    ultestIotFlashStartOffset,
                                    &uctestIotFlashWriteBuffer[ 0 ],
                                    testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
    TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Write partial page with invalid start address */
        lRetVal = iot_flash_write_sync( xFlashHandle,
                                        -1,
                                        &uctestIotFlashWriteBuffer[ 0 ],
                                        testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

        /* Write partial page with invalid buffer */
        lRetVal = iot_flash_write_sync( xFlashHandle,
                                        ultestIotFlashStartOffset,
                                        NULL,
                                        testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}

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

/**
 * @brief Test Function to test fuzzing of iot_flash_read
 *
 */
TEST( TEST_IOT_FLASH, AFQP_IotFlashReadFuzz )
{
    IotFlashHandle_t xFlashHandle;
    int32_t lRetVal;

    /* Read partial page with invalid handle */
    lRetVal = iot_flash_read_sync( NULL,
                                   ultestIotFlashStartOffset,
                                   &uctestIotFlashReadBuffer[ 0 ],
                                   testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
    TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

    /* Open the flash instance */
    xFlashHandle = iot_flash_open( ltestIotFlashInstance );
    TEST_ASSERT_NOT_EQUAL( NULL, xFlashHandle );

    if( TEST_PROTECT() )
    {
        /* Read partial page with invalid start address */
        lRetVal = iot_flash_read_sync( xFlashHandle,
                                       -1,
                                       &uctestIotFlashReadBuffer[ 0 ],
                                       testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );

        /* Read partial page with invalid buffer */
        lRetVal = iot_flash_read_sync( xFlashHandle,
                                       ultestIotFlashStartOffset,
                                       NULL,
                                       testIotFLASH_DEFAULT_PARTIAL_PAGE_SIZE );
        TEST_ASSERT_EQUAL( IOT_FLASH_INVALID_VALUE, lRetVal );
    }

    /* Close flash handle */
    lRetVal = iot_flash_close( xFlashHandle );
    TEST_ASSERT_EQUAL( IOT_FLASH_SUCCESS, lRetVal );
}