// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if defined(GB_MEASURE_MEMORY_FOR_THIS)
#undef GB_MEASURE_MEMORY_FOR_THIS
#endif

#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif

#include "azure_macro_utils/macro_utils.h"
#include "azure_c_shared_utility/optimize_size.h"
#include "azure_c_shared_utility/gballoc.h"
#include "testrunnerswitcher.h"
#include "azure_c_shared_utility/lock.h"

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)~(size_t)0)
#endif

static TEST_MUTEX_HANDLE g_testByTest;

static void* TEST_ALLOC_PTR1 = (void*)0x4242;
static void* TEST_ALLOC_PTR2 = (void*)0x4243;
static void* TEST_REALLOC_PTR = (void*)0x4245;

#define OVERHEAD_SIZE    4096
static const LOCK_HANDLE TEST_LOCK_HANDLE = (LOCK_HANDLE)0x4244;

#define ENABLE_MOCKS

#include "umock_c/umock_c.h"
#include "umock_c/umock_c_prod.h"

IMPLEMENT_UMOCK_C_ENUM_TYPE(LOCK_RESULT, LOCK_RESULT_VALUES);

#ifdef __cplusplus
extern "C" {
#endif
    MOCKABLE_FUNCTION(, void*, mock_malloc, size_t, size);
    MOCKABLE_FUNCTION(, void*, mock_calloc, size_t, nmemb, size_t, size);
    MOCKABLE_FUNCTION(, void*, mock_realloc, void*, ptr, size_t, size);
    MOCKABLE_FUNCTION(, void, mock_free, void*, ptr);

    MOCKABLE_FUNCTION(, LOCK_HANDLE, Lock_Init);
    MOCKABLE_FUNCTION(, LOCK_RESULT, Lock_Deinit, LOCK_HANDLE, handle);
    MOCKABLE_FUNCTION(, LOCK_RESULT, Lock, LOCK_HANDLE, handle);
    MOCKABLE_FUNCTION(, LOCK_RESULT, Unlock, LOCK_HANDLE, handle);
#ifdef __cplusplus
}
#endif

MU_DEFINE_ENUM_STRINGS(UMOCK_C_ERROR_CODE, UMOCK_C_ERROR_CODE_VALUES)

static void on_umock_c_error(UMOCK_C_ERROR_CODE error_code)
{
    ASSERT_FAIL("umock_c reported error :%" PRI_MU_ENUM "", MU_ENUM_VALUE(UMOCK_C_ERROR_CODE, error_code));
}

BEGIN_TEST_SUITE(GBAlloc_UnitTests)

TEST_SUITE_INITIALIZE(TestClassInitialize)
{
    int result;

    g_testByTest = TEST_MUTEX_CREATE();
    ASSERT_IS_NOT_NULL(g_testByTest);

    result = umock_c_init(on_umock_c_error);
    ASSERT_ARE_EQUAL(int, 0, result);

    REGISTER_UMOCK_ALIAS_TYPE(LOCK_HANDLE, void*);
    REGISTER_TYPE(LOCK_RESULT, LOCK_RESULT);

    REGISTER_GLOBAL_MOCK_RETURN(mock_malloc, TEST_ALLOC_PTR1);
    REGISTER_GLOBAL_MOCK_RETURN(mock_realloc, TEST_ALLOC_PTR1);
    REGISTER_GLOBAL_MOCK_RETURN(mock_calloc, TEST_ALLOC_PTR1);

    REGISTER_GLOBAL_MOCK_RETURN(Lock_Init, TEST_LOCK_HANDLE);
    REGISTER_GLOBAL_MOCK_RETURN(Lock, LOCK_OK);
    REGISTER_GLOBAL_MOCK_RETURN(Unlock, LOCK_OK);
}

TEST_SUITE_CLEANUP(TestClassCleanup)
{
    umock_c_deinit();
    TEST_MUTEX_DESTROY(g_testByTest);
}

TEST_FUNCTION_INITIALIZE(TestMethodInitialize)
{
    if (TEST_MUTEX_ACQUIRE(g_testByTest))
    {
        ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework");
    }

    umock_c_reset_all_calls();
}

TEST_FUNCTION_CLEANUP(TestMethodCleanup)
{
    gballoc_deinit();

    TEST_MUTEX_RELEASE(g_testByTest);
}

/* gballoc_init */

/* Tests_SRS_GBALLOC_01_002: [Upon initialization the total memory used and maximum total memory used tracked by the module shall be set to 0.] */
TEST_FUNCTION(gballoc_init_resets_memory_used)
{
    //arrange
    void* allocation;
    gballoc_init();
    allocation = malloc(OVERHEAD_SIZE);

    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    gballoc_free(gballoc_malloc(1));

    gballoc_deinit();

    // act
    gballoc_init();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getCurrentMemoryUsed());

    ///cleanup
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_024: [gballoc_init shall initialize the gballoc module and return 0 upon success.] */
/* Tests_SRS_GBALLOC_01_026: [gballoc_Init shall create a lock handle that will be used to make the other gballoc APIs thread-safe.] */
TEST_FUNCTION(when_gballoc_init_calls_lock_init_and_it_succeeds_then_gballoc_init_succeeds)
{
    // arrange
    int result;
    STRICT_EXPECTED_CALL(Lock_Init());

    // act
    result = gballoc_init();

    // assert
    ASSERT_ARE_EQUAL(int, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_027: [If the Lock creation fails, gballoc_init shall return a non-zero value.] */
TEST_FUNCTION(when_lock_init_fails_gballoc_init_fails)
{
    // arrange
    int result;
    STRICT_EXPECTED_CALL(Lock_Init())
        .SetReturn((LOCK_HANDLE)NULL);

    // act
    result = gballoc_init();

    // assert
    ASSERT_ARE_NOT_EQUAL(int, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_025: [Init after Init shall fail and return a non-zero value.] */
TEST_FUNCTION(gballoc_init_after_gballoc_init_fails)
{
    // arrange
    int result;
    STRICT_EXPECTED_CALL(Lock_Init());
    gballoc_init();

    //act
    result = gballoc_init();

    // assert
    ASSERT_ARE_NOT_EQUAL(int, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* gballoc_deinit */

/* Tests_SRS_GBALLOC_01_028: [gballoc_deinit shall free all resources allocated by gballoc_init.] */
TEST_FUNCTION(gballoc_deinit_frees_the_lock_when_the_module_was_initialized)
{
    // arrange
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock_Deinit(TEST_LOCK_HANDLE));

    // act
    gballoc_deinit();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_029: [if gballoc is not initialized gballoc_deinit shall do nothing.] */
TEST_FUNCTION(gballoc_deinit_after_gballoc_deinit_doesnot_free_lock)
{
    //arrange

    // act
    gballoc_deinit();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* gballoc_malloc */

/* Tests_SRS_GBALLOC_01_048: [If acquiring the lock fails, gballoc_malloc shall return NULL.] */
TEST_FUNCTION(when_acquiring_the_lock_fails_gballoc_malloc_fails)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_malloc(1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_003: [gballoc_malloc shall call the C99 malloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_004: [If the underlying malloc call is successful, gballoc_malloc shall increment the total memory used with the amount indicated by size.] */
/* Tests_SRS_GBALLOC_01_030: [gballoc_malloc shall ensure thread safety by using the lock created by gballoc_Init.] */
TEST_FUNCTION(gballoc_malloc_with_0_Size_Calls_Underlying_malloc)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_malloc(0));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_malloc(0);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_003: [gballoc_malloc shall call the C99 malloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_004: [If the underlying malloc call is successful, gballoc_malloc shall increment the total memory used with the amount indicated by size.] */
TEST_FUNCTION(gballoc_malloc_with_1_Size_Calls_Underlying_malloc_And_Increases_Max_Used)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_malloc(1);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_012: [When the underlying malloc call fails, gballoc_malloc shall return NULL and size should not be counted towards total memory used.] */
TEST_FUNCTION(When_malloc_Fails_Then_gballoc_malloc_fails_too)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_malloc(1))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(mock_free(allocation));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_malloc(1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_013: [When gballoc_malloc fails allocating memory for its internal use, gballoc_malloc shall return NULL.] */
TEST_FUNCTION(When_allocating_memory_for_tracking_information_fails_Then_gballoc_malloc_fails_too)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_malloc(1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());
}

/* Tests_SRS_GBALLOC_01_039: [If gballoc was not initialized gballoc_malloc shall simply call malloc without any memory tracking being performed.] */
TEST_FUNCTION(gballoc_malloc_after_deinit_calls_crt_malloc)
{
    // arrange
    void* result;
    STRICT_EXPECTED_CALL(mock_malloc(1));

    //act
    result = gballoc_malloc(1);

    //assert
    ASSERT_IS_NOT_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* gballoc_calloc */

/* Tests_SRS_GBALLOC_01_046: [If acquiring the lock fails, gballoc_calloc shall return NULL.] */
TEST_FUNCTION(when_acquiring_the_lock_fails_gballoc_calloc_fails)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
    .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_calloc(1,1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
/* Tests_SRS_GBALLOC_01_031: [gballoc_calloc shall ensure thread safety by using the lock created by gballoc_Init] */
TEST_FUNCTION(gballoc_calloc_with_0_Size_And_ItemCount_Calls_Underlying_calloc)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(0, 0));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(0, 0);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
TEST_FUNCTION(gballoc_calloc_with_1_Item_Of_1_Size_Calls_Underlying_malloc_And_Increases_Max_Used)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(1, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(1, 1);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
TEST_FUNCTION(gballoc_calloc_with_1_Item_Of_0_Size_Calls_Underlying_malloc_And_Does_Not_Increase_Max_Used)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(1, 0));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(1, 0);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
TEST_FUNCTION(gballoc_calloc_with_0_Items_Of_1_Size_Calls_Underlying_malloc_And_Does_Not_Increase_Max_Used)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(0, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(0, 1);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_020: [gballoc_calloc shall call the C99 calloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_021: [If the underlying calloc call is successful, gballoc_calloc shall increment the total memory used with nmemb*size.] */
TEST_FUNCTION(gballoc_calloc_with_42_Items_Of_2_Size_Calls_Underlying_malloc_And_Increases_Max_Size)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(42, 2));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(42, 2);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 84, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_022: [When the underlying calloc call fails, gballoc_calloc shall return NULL and size should not be counted towards total memory used.] */
TEST_FUNCTION(When_calloc_Fails_Then_gballoc_calloc_fails_too)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_calloc(1, 1))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(mock_free(allocation));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(1, 1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_023: [When gballoc_calloc fails allocating memory for its internal use, gballoc_calloc shall return NULL.] */
TEST_FUNCTION(When_allocating_memory_for_tracking_information_fails_Then_gballoc_calloc_fails_too)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_calloc(1, 1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());
}

/* Tests_SRS_GBALLOC_01_040: [If gballoc was not initialized gballoc_calloc shall simply call calloc without any memory tracking being performed.] */
TEST_FUNCTION(gballoc_calloc_after_deinit_calls_crt_calloc)
{
    // arrange
    void* result;
    STRICT_EXPECTED_CALL(mock_calloc(1, 1));

    // act
    result = gballoc_calloc(1, 1);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_IS_NOT_NULL(result);
}


/* gballoc_realloc */

/* Tests_SRS_GBALLOC_01_047: [If acquiring the lock fails, gballoc_realloc shall return NULL.]*/
TEST_FUNCTION(when_acquiring_the_lock_fails_gballoc_realloc_fails)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_realloc(NULL, 1);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_IS_NULL(result);
}

/* Tests_SRS_GBALLOC_01_041: [If gballoc was not initialized gballoc_realloc shall shall simply call realloc without any memory tracking being performed.] */
TEST_FUNCTION(gballoc_realloc_after_deinit_fails)
{
    // arrange
    void* result;
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));

    // act
    result = gballoc_realloc(NULL, 1);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_IS_NOT_NULL(result);
}

/* Tests_SRS_GBALLOC_01_005: [gballoc_realloc shall call the C99 realloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_017: [When ptr is NULL, gballoc_realloc shall call the underlying realloc with ptr being NULL and the realloc result shall be tracked by gballoc.] */
/* Tests_SRS_GBALLOC_01_032: [gballoc_realloc shall ensure thread safety by using the lock created by gballoc_Init.] */
TEST_FUNCTION(gballoc_realloc_with_NULL_Arg_And_0_Size_Calls_Underlying_realloc)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 0));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_realloc(NULL, 0);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_005: [gballoc_realloc shall call the C99 realloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_017: [When ptr is NULL, gballoc_realloc shall call the underlying realloc with ptr being NULL and the realloc result shall be tracked by gballoc.] */
TEST_FUNCTION(gballoc_realloc_with_NULL_Arg_And_1_Size_Calls_Underlying_realloc)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_realloc(NULL, 1);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_005: [gballoc_realloc shall call the C99 realloc function and return its result.] */
/* Tests_SRS_GBALLOC_01_006: [If the underlying realloc call is successful, gballoc_realloc shall look up the size associated with the pointer ptr and decrease the total memory used with that size.] */
TEST_FUNCTION(gballoc_realloc_with_Previous_1_Byte_Block_Ptr_And_2_Size_Calls_Underlying_realloc_And_Increases_Max_Used_Memory)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(mock_realloc(TEST_ALLOC_PTR1, 2))
        .SetReturn(TEST_ALLOC_PTR2);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    result = gballoc_realloc(NULL, 1);

    // act
    result = gballoc_realloc(result, 2);

    // assert
    ASSERT_ARE_EQUAL(void_ptr, TEST_ALLOC_PTR2, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 2, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_014: [When the underlying realloc call fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */
TEST_FUNCTION(When_realloc_fails_then_gballoc_realloc_Fails_Too_And_No_Change_Is_Made_To_Memory_Counters)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(mock_realloc(TEST_ALLOC_PTR1, 2))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    result = gballoc_realloc(NULL, 1);

    // act
    result = gballoc_realloc(result, 2);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(TEST_ALLOC_PTR1);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_015: [When allocating memory used for tracking by gballoc_realloc fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */
TEST_FUNCTION(When_Allocating_Memory_For_tracking_fails_gballoc_realloc_fails)
{
    // arrange
    void* result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_realloc(NULL, 1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());
}

/* Tests_SRS_GBALLOC_01_016: [When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_realloc shall return NULL and the underlying realloc shall not be called.] */
TEST_FUNCTION(When_The_Pointer_Is_Not_Tracked_gballoc_realloc_Returns_NULL)
{
    // arrange
    void* result1;
    void* result2;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    result1 = gballoc_realloc(NULL, 1);

    // act
    result2 = gballoc_realloc(TEST_REALLOC_PTR, 2);

    // assert
    ASSERT_IS_NULL(result2);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(result1);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_014: [When the underlying realloc call fails, gballoc_realloc shall return NULL and no change should be made to the counted total memory usage.] */
TEST_FUNCTION(When_ptr_is_null_and_the_underlying_realloc_fails_then_the_memory_used_for_tracking_is_freed)
{
    // arrange
    void* result;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1))
        .SetReturn((void*)NULL);
    STRICT_EXPECTED_CALL(mock_free(allocation));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_realloc(NULL, 1);

    // assert
    ASSERT_IS_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 0, gballoc_getMaximumMemoryUsed());

    // cleanup
    free(allocation);
}

/* gballoc_free */

/* Tests_SRS_GBALLOC_01_042: [If gballoc was not initialized gballoc_free shall shall simply call free.] */
TEST_FUNCTION(gballoc_free_after_deinit_calls_crt_free)
{
    // arrange
    STRICT_EXPECTED_CALL(mock_free((void*)0x4242));

    // act
    gballoc_free((void*)0x4242);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_049: [If acquiring the lock fails, gballoc_free shall do nothing.] */
TEST_FUNCTION(when_acquiring_the_lock_fails_then_gballoc_free_does_nothing)
{
    // arrange
    void* block;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    block = gballoc_realloc(NULL, 1);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    gballoc_free(block);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    gballoc_free(block);
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_008: [gballoc_free shall call the C99 free function.] */
/* Tests_SRS_GBALLOC_01_009: [gballoc_free shall also look up the size associated with the ptr pointer and decrease the total memory used with the associated size amount.] */
/* Tests_SRS_GBALLOC_01_010: [gballoc_getMaximumMemoryUsed shall return the maximum amount of total memory used recorded since the module initialization.] */
/* Tests_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */
/* Tests_SRS_GBALLOC_01_033: [gballoc_free shall ensure thread safety by using the lock created by gballoc_Init.] */
TEST_FUNCTION(gballoc_free_calls_the_underlying_free)
{
    // arrange
    void* block;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    /* This is the call to the underlying malloc with the size we want to allocate */
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    block = gballoc_realloc(NULL, 1);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(mock_free(TEST_ALLOC_PTR1));
    STRICT_EXPECTED_CALL(mock_free(allocation));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    gballoc_free(block);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_008: [gballoc_free shall call the C99 free function.] */
/* Tests_SRS_GBALLOC_01_009: [gballoc_free shall also look up the size associated with the ptr pointer and decrease the total memory used with the associated size amount.] */
/* Tests_SRS_GBALLOC_01_010: [gballoc_getMaximumMemoryUsed shall return the maximum amount of total memory used recorded since the module initialization.] */
/* Tests_SRS_GBALLOC_01_010: [gballoc_getMaximumMemoryUsed shall return the maximum amount of total memory used recorded since the module initialization.] */
/* Tests_SRS_GBALLOC_01_011: [The maximum total memory used shall be the maximum of the total memory used at any point.] */
TEST_FUNCTION(gballoc_malloc_free_2_times_with_1_byte_yields_1_byte_as_max)
{
    // arrange
    void* block;
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_realloc(NULL, 1));
    block = gballoc_realloc(NULL, 1);
    gballoc_free(block);

    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    block = gballoc_realloc(NULL, 1);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(mock_free(TEST_ALLOC_PTR1));
    STRICT_EXPECTED_CALL(mock_free(allocation));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    gballoc_free(block);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, 1, gballoc_getMaximumMemoryUsed());

    // cleanup
    free(allocation);
}

/* Tests_SRS_GBALLOC_01_019:[When the ptr pointer cannot be found in the pointers tracked by gballoc, gballoc_free shall not free any memory.] */
TEST_FUNCTION(gballoc_free_with_an_untracked_pointer_does_not_alter_total_memory_used)
{
    // arrange
    void* allocation;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    /* don't quite like this, but I'm unsure I want to invest more in this memory counting */
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);

    gballoc_realloc(NULL, 1);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    gballoc_free(TEST_REALLOC_PTR);

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    gballoc_realloc(TEST_ALLOC_PTR1, 2);
    ASSERT_ARE_EQUAL(size_t, 2, gballoc_getMaximumMemoryUsed());

    // cleanup
    gballoc_free(TEST_ALLOC_PTR1);
    free(allocation);
}

/* gballoc_getMaximumMemoryUsed */


/* Tests_SRS_GBALLOC_01_034: [gballoc_getMaximumMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.] */
TEST_FUNCTION(when_gballoc_getMaximumMemoryUsed_called_It_Shall_Lock_And_Unlock)
{
    // arrange
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    // act
    (void)gballoc_getMaximumMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_050: [If the lock cannot be acquired, gballoc_getMaximumMemoryUsed shall return SIZE_MAX.] */
TEST_FUNCTION(when_acquiring_the_lock_fails_then_gballoc_getMaximumMemoryUsed_fails)
{
    // arrange
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_getMaximumMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
    ASSERT_ARE_EQUAL(size_t, SIZE_MAX, result);
}

/* Tests_SRS_GBALLOC_01_038: [If gballoc was not initialized gballoc_getMaximumMemoryUsed shall return MAX_INT_SIZE.]  */
TEST_FUNCTION(gballoc_getMaximumMemoryUsed_after_deinit_fails)
{
    // arrange

    // act
    size_t result = gballoc_getMaximumMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, SIZE_MAX, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* gballoc_getCurrentMemoryUsed */

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_returns_1)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    toBeFreed = gballoc_malloc(1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed);
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_2x3_byte_calloc_returns_6)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    toBeFreed = gballoc_calloc(2, 3);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 6, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed);
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_3_bytes_realloc_returns_3)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed = gballoc_malloc(1);
    toBeFreed = gballoc_realloc(toBeFreed, 3);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 3, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed);
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_free_returns_0)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed = gballoc_malloc(1);
    gballoc_free(toBeFreed);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_2x3_byte_calloc_and_free_returns_0)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed = gballoc_calloc(2, 3);
    gballoc_free(toBeFreed);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_realloc_and_free_returns_0)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed = gballoc_malloc(1);
    toBeFreed = gballoc_realloc(toBeFreed, 3);
    gballoc_free(toBeFreed);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    free(allocation);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_1_byte_malloc_returns_2)
{
    // arrange
    void* allocation1;
    void* allocation2;
    void* toBeFreed1;
    void* toBeFreed2;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation1 = malloc(OVERHEAD_SIZE);
    allocation2 = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation1);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation2);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed1 = gballoc_malloc(1);
    toBeFreed2 = gballoc_malloc(1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 2, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed1);
    gballoc_free(toBeFreed2);
    free(allocation1);
    free(allocation2);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_1_byte_malloc_and_3_realloc_returns_4)
{
    // arrange
    void* allocation1;
    void* allocation2;
    void* toBeFreed1;
    void* toBeFreed2;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation1 = malloc(OVERHEAD_SIZE);
    allocation2 = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation1);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation2);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed1 = gballoc_malloc(1);
    toBeFreed2 = gballoc_malloc(1);
    toBeFreed2 = gballoc_realloc(toBeFreed2, 3);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 4, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed1);
    gballoc_free(toBeFreed2);
    free(allocation1);
    free(allocation2);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_6_byte_calloc_returns_7)
{
    // arrange
    void* allocation1;
    void* allocation2;
    void* toBeFreed1;
    void* toBeFreed2;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation1 = malloc(OVERHEAD_SIZE);
    allocation2 = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation1);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation2);
    STRICT_EXPECTED_CALL(mock_calloc(2, 3));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed1 = gballoc_malloc(1);
    toBeFreed2 = gballoc_calloc(2, 3);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 7, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed1);
    gballoc_free(toBeFreed2);
    free(allocation1);
    free(allocation2);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_6_byte_calloc_and_free_returns_6)
{
    // arrange
    void* allocation1;
    void* allocation2;
    void* toBeFreed1;
    void* toBeFreed2;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation1 = malloc(OVERHEAD_SIZE);
    allocation2 = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation1);
    STRICT_EXPECTED_CALL(mock_malloc(1))
        .SetReturn((char*)allocation1 + OVERHEAD_SIZE / 2); /*somewhere in the middle of allocation*/
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation2);
    STRICT_EXPECTED_CALL(mock_calloc(2, 3))
        .SetReturn((char*)allocation2 + OVERHEAD_SIZE / 2);
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed1 = gballoc_malloc(1);
    toBeFreed2 = gballoc_calloc(2, 3);
    gballoc_free(toBeFreed1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 6, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed2);
    free(allocation1);
    free(allocation2);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/*Tests_SRS_GBALLOC_02_001: [gballoc_getCurrentMemoryUsed shall return the currently used memory size.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_1_byte_malloc_and_1_byte_malloc_and_3_realloc_and_free_returns_3)
{
    // arrange
    void* allocation1;
    void* allocation2;
    void* toBeFreed1;
    void* toBeFreed2;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();
    allocation1 = malloc(OVERHEAD_SIZE);
    allocation2 = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation1);
    STRICT_EXPECTED_CALL(mock_malloc(1))
        .SetReturn((char*)allocation1 + OVERHEAD_SIZE / 2); /*somewhere in the middle of allocation*/
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation2);
    STRICT_EXPECTED_CALL(mock_realloc(IGNORED_PTR_ARG, 3))
        .IgnoreArgument(1)
        .SetReturn((char*)allocation2 + OVERHEAD_SIZE / 2); /*somewhere in the middle of allocation*/
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    toBeFreed1 = gballoc_malloc(1);
    toBeFreed2 = gballoc_malloc(1);
    toBeFreed2 = gballoc_realloc(toBeFreed2, 3);
    gballoc_free(toBeFreed1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 3, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    ///cleanup
    gballoc_free(toBeFreed2);
    free(allocation1);
    free(allocation2);
    umock_c_reset_all_calls(); //this is just for mathematics, not for functionality
}

/* Tests_SRS_GBALLOC_01_036: [gballoc_getCurrentMemoryUsed shall ensure thread safety by using the lock created by gballoc_Init.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_locks_and_unlocks)
{
    // assert
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_044: [If gballoc was not initialized gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */
TEST_FUNCTION(gballoc_getCurrentMemoryUsed_after_deinit_fails)
{
    // arrange

    // act
    size_t result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, SIZE_MAX, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_01_051: [If the lock cannot be acquired, gballoc_getCurrentMemoryUsed shall return SIZE_MAX.] */
TEST_FUNCTION(when_acquiring_the_lock_fails_gballoc_getCurrentMemoryUsed_fails)
{
    // arrange
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_getCurrentMemoryUsed();

    // assert
    ASSERT_ARE_EQUAL(size_t, SIZE_MAX, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_07_001: [ If gballoc was not initialized gballoc_getAllocationCount shall return SIZE_MAX. ] */
TEST_FUNCTION(gballoc_getAllocationCount_without_init_fail)
{
    // arrange
    size_t result;

    // act
    result = gballoc_getAllocationCount();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_07_003: [ If the lock cannot be acquired, gballoc_getAllocationCount shall return SIZE_MAX. ] */
TEST_FUNCTION(gballoc_getAllocationCount_lock_fail)
{
    // arrange
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    result = gballoc_getAllocationCount();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_07_004: [ gballoc_getAllocationCount shall return the currently number of allocations. ] */
/* Tests_SRS_GBALLOC_07_002: [ gballoc_getAllocationCount shall ensure thread safety by using the lock created by gballoc_Init ] */
TEST_FUNCTION(gballoc_getAllocationCount_success)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t result;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    toBeFreed = gballoc_malloc(1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    result = gballoc_getAllocationCount();

    // assert
    ASSERT_ARE_EQUAL(size_t, 1, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // Cleanup
    gballoc_free(toBeFreed);
    free(allocation);
}

TEST_FUNCTION(gballoc_resetMetrics_without_init_fail)
{
    // arrange

    // act
    gballoc_resetMetrics();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_07_003: [ If the lock cannot be acquired, gballoc_getAllocationCount shall return SIZE_MAX. ] */
TEST_FUNCTION(gballoc_resetMetrics_lock_fail)
{
    // arrange
    gballoc_init();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE))
        .SetReturn(LOCK_ERROR);

    // act
    gballoc_resetMetrics();

    // assert
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}

/* Tests_SRS_GBALLOC_07_004: [ gballoc_getAllocationCount shall return the currently number of allocations. ] */
/* Tests_SRS_GBALLOC_07_002: [ gballoc_getAllocationCount shall ensure thread safety by using the lock created by gballoc_Init ] */
TEST_FUNCTION(gballoc_resetMetrics_success)
{
    // arrange
    void* allocation;
    void* toBeFreed;
    size_t mem_used;
    size_t alloc_count;
    gballoc_init();
    umock_c_reset_all_calls();

    allocation = malloc(OVERHEAD_SIZE);

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    EXPECTED_CALL(mock_calloc(1, 0))
        .SetReturn(allocation);
    STRICT_EXPECTED_CALL(mock_malloc(1));
    toBeFreed = gballoc_malloc(1);
    umock_c_reset_all_calls();
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    alloc_count = gballoc_getAllocationCount();
    ASSERT_ARE_EQUAL(size_t, 1, alloc_count);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Lock(TEST_LOCK_HANDLE));
    STRICT_EXPECTED_CALL(Unlock(TEST_LOCK_HANDLE));

    // act
    gballoc_resetMetrics();
    mem_used = gballoc_getCurrentMemoryUsed();
    alloc_count = gballoc_getAllocationCount();

    // assert
    ASSERT_ARE_EQUAL(size_t, 0, mem_used);
    ASSERT_ARE_EQUAL(size_t, 0, alloc_count);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // Cleanup
    gballoc_free(toBeFreed);
    free(allocation);
}

END_TEST_SUITE(GBAlloc_UnitTests)
