text: // Copyright (c) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  #ifdef __cplusplus
  #include <cstdint>
  #include <cstdlib>
  #else
  #include <stdlib.h>
  #include <stdint.h>
  #include <stdbool.h>
  #endif
  
  #include "azure_macro_utils/macro_utils.h"
  #include "testrunnerswitcher.h"
  #include "umock_c/umock_c.h"
  #include "umock_c/umocktypes_stdint.h"
  #include "umock_c/umocktypes_bool.h"
  
  static void* my_gballoc_malloc(size_t size)
  {
      return malloc(size);
  }
  
  static void* my_gballoc_calloc(size_t nmemb, size_t size)
  {
      return calloc(nmemb, size);
  }
  
  static void* my_gballoc_realloc(void* ptr, size_t size)
  {
      return realloc(ptr, size);
  }
  
  static void my_gballoc_free(void* ptr)
  {
      free(ptr);
  }
  
  #define ENABLE_MOCKS
  
  #include "azure_c_shared_utility/gballoc.h"
  #include "azure_c_shared_utility/singlylinkedlist.h"
  #include "azure_uamqp_c/amqpvalue.h"
  
  #undef ENABLE_MOCKS
  
  #include "azure_uamqp_c/frame_codec.h"
  
  #define TEST_DESCRIPTION_AMQP_VALUE        (AMQP_VALUE)0x4243
  #define TEST_LIST_HANDLE                (SINGLYLINKEDLIST_HANDLE)0x4246
  #define TEST_SUBSCRIPTION_ITEM            (void*)0x4247
  #define TEST_ERROR_CONTEXT                (void*)0x4248
  #define TEST_LIST_ITEM_HANDLE            (LIST_ITEM_HANDLE)0x4249
  
  typedef struct TEST_LIST_ITEM_TAG
  {
      const void* item_value;
  } TEST_LIST_ITEM;
  
  static TEST_LIST_ITEM** list_items = NULL;
  static size_t list_item_count = 0;
  static unsigned char* sent_io_bytes;
  static size_t sent_io_byte_count;
  static char expected_stringified_io[8192];
  static char actual_stringified_io[8192];
  
  void stringify_bytes(const unsigned char* bytes, size_t byte_count, char* output_string)
  {
      size_t i;
      size_t pos = 0;
  
      output_string[pos++] = '[';
      for (i = 0; i < byte_count; i++)
      {
          (void)sprintf(&output_string[pos], "0x%02X", bytes[i]);
          if (i < byte_count - 1)
          {
              strcat(output_string, ",");
          }
          pos = strlen(output_string);
      }
      output_string[pos++] = ']';
      output_string[pos++] = '\0';
  }
  
      /* frame received callback */
  MOCK_FUNCTION_WITH_CODE(, void, on_frame_received_1, void*, context, const unsigned char*, type_specific, uint32_t, type_specific_size, const unsigned char*, frame_body, uint32_t, frame_body_size)
  MOCK_FUNCTION_END();
  MOCK_FUNCTION_WITH_CODE(, void, on_frame_received_2, void*, context, const unsigned char*, type_specific, uint32_t, type_specific_size, const unsigned char*, frame_body, uint32_t, frame_body_size)
  MOCK_FUNCTION_END();
  MOCK_FUNCTION_WITH_CODE(, void, test_frame_codec_decode_error, void*, context)
  MOCK_FUNCTION_END();
  
  MOCK_FUNCTION_WITH_CODE(, void, test_on_bytes_encoded, void*, context, const unsigned char*, bytes, size_t, length, bool, encode_complete)
      unsigned char* new_bytes = (unsigned char*)my_gballoc_realloc(sent_io_bytes, sent_io_byte_count + length);
      if (new_bytes != NULL)
      {
          sent_io_bytes = new_bytes;
          (void)memcpy(sent_io_bytes + sent_io_byte_count, bytes, length);
          sent_io_byte_count += length;
      }
  MOCK_FUNCTION_END();
  
  static LIST_ITEM_HANDLE my_singlylinkedlist_add(SINGLYLINKEDLIST_HANDLE list, const void* item)
  {
      TEST_LIST_ITEM** items = (TEST_LIST_ITEM**)my_gballoc_realloc(list_items, (list_item_count + 1) * sizeof(TEST_LIST_ITEM*));
      LIST_ITEM_HANDLE to_return = NULL;
      (void)list;
      if (items != NULL)
      {
          list_items = items;
          list_items[list_item_count] = (TEST_LIST_ITEM*)my_gballoc_malloc(sizeof(TEST_LIST_ITEM));
          if (list_items[list_item_count] != NULL)
          {
              list_items[list_item_count]->item_value = item;
              to_return = (LIST_ITEM_HANDLE)list_items[list_item_count];
              list_item_count++;
          }
      }
      return to_return;
  }
  
  static const void* my_singlylinkedlist_item_get_value(LIST_ITEM_HANDLE item_handle)
  {
      return ((TEST_LIST_ITEM*)item_handle)->item_value;
  }
  
  static LIST_ITEM_HANDLE my_singlylinkedlist_find(SINGLYLINKEDLIST_HANDLE handle, LIST_MATCH_FUNCTION match_function, const void* match_context)
  {
      size_t i;
      LIST_ITEM_HANDLE found_item = NULL;
      (void)handle;
  
      for (i = 0; i < list_item_count; i++)
      {
          if (match_function((LIST_ITEM_HANDLE)list_items[i], match_context))
          {
              found_item = (LIST_ITEM_HANDLE)list_items[i];
              break;
          }
      }
      return found_item;
  }
  
  static int my_singlylinkedlist_remove(SINGLYLINKEDLIST_HANDLE list, LIST_ITEM_HANDLE list_item)
  {
      size_t i;
      (void)list;
  
      for (i = 0; i < list_item_count; i++)
      {
          if (((LIST_ITEM_HANDLE)list_items[i]) == list_item)
          {
              break;
          }
      }
      if (i < list_item_count)
      {
          my_gballoc_free(list_items[i]);
          memmove(&list_items[i], &list_items[i + 1], (list_item_count - i - 1) * sizeof(TEST_LIST_ITEM*));
          list_item_count--;
          if (list_item_count == 0)
          {
              my_gballoc_free(list_items);
              list_items = NULL;
          }
      }
      return 0;
  }
  
  static TEST_MUTEX_HANDLE g_testByTest;
  
  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(frame_codec_ut)
  
  TEST_SUITE_INITIALIZE(suite_init)
  {
      int result;
  
      g_testByTest = TEST_MUTEX_CREATE();
      ASSERT_IS_NOT_NULL(g_testByTest);
  
      umock_c_init(on_umock_c_error);
  
      result = umocktypes_stdint_register_types();
      ASSERT_ARE_EQUAL(int, 0, result, "Failed registering stdint types");
      result = umocktypes_bool_register_types();
      ASSERT_ARE_EQUAL(int, 0, result, "Failed registering bool types");
  
      REGISTER_GLOBAL_MOCK_HOOK(gballoc_malloc, my_gballoc_malloc);
      REGISTER_GLOBAL_MOCK_HOOK(gballoc_calloc, my_gballoc_calloc);
      REGISTER_GLOBAL_MOCK_HOOK(gballoc_free, my_gballoc_free);
      REGISTER_GLOBAL_MOCK_HOOK(gballoc_realloc, my_gballoc_realloc);
      REGISTER_GLOBAL_MOCK_RETURN(singlylinkedlist_create, TEST_LIST_HANDLE);
      REGISTER_GLOBAL_MOCK_HOOK(singlylinkedlist_add, my_singlylinkedlist_add);
      REGISTER_GLOBAL_MOCK_HOOK(singlylinkedlist_item_get_value, my_singlylinkedlist_item_get_value);
      REGISTER_GLOBAL_MOCK_HOOK(singlylinkedlist_find, my_singlylinkedlist_find);
      REGISTER_GLOBAL_MOCK_HOOK(singlylinkedlist_remove, my_singlylinkedlist_remove);
  
      REGISTER_UMOCK_ALIAS_TYPE(SINGLYLINKEDLIST_HANDLE, void*);
      REGISTER_UMOCK_ALIAS_TYPE(LIST_MATCH_FUNCTION, void*);
      REGISTER_UMOCK_ALIAS_TYPE(LIST_ITEM_HANDLE, void*);
  }
  
  TEST_SUITE_CLEANUP(suite_cleanup)
  {
      umock_c_deinit();
  
      TEST_MUTEX_DESTROY(g_testByTest);
  }
  
  TEST_FUNCTION_INITIALIZE(method_init)
  {
      if (TEST_MUTEX_ACQUIRE(g_testByTest))
      {
          ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework");
      }
  
      umock_c_reset_all_calls();
  }
  
  TEST_FUNCTION_CLEANUP(method_cleanup)
  {
      size_t i;
      if (sent_io_bytes != NULL)
      {
          free(sent_io_bytes);
          sent_io_bytes = NULL;
      }
      for (i = 0; i < list_item_count; i++)
      {
          free(list_items[i]);
      }
      free(list_items);
      list_items = NULL;
      list_item_count = 0;
  
      sent_io_byte_count = 0;
  
      TEST_MUTEX_RELEASE(g_testByTest);
  }
  
  /* frame_codec_create */
  
  /* Tests_SRS_FRAME_CODEC_01_021: [frame_codec_create shall create a new instance of frame_codec and return a non-NULL handle to it on success.] */
  TEST_FUNCTION(frame_codec_create_with_valid_args_succeeds)
  {
      // arrange
      FRAME_CODEC_HANDLE frame_codec;
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_create());
  
      // act
      frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
  
      // assert
      ASSERT_IS_NOT_NULL(frame_codec);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_020: [If the on_frame_codec_error argument is NULL, frame_codec_create shall return NULL.] */
  TEST_FUNCTION(frame_codec_create_with_NULL_on_error_decode_fails)
  {
      // arrange
  
      // act
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(NULL, TEST_ERROR_CONTEXT);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_IS_NULL(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_104: [The callback_context shall be allowed to be NULL.] */
  TEST_FUNCTION(frame_codec_create_with_NULL_frame_codec_decode_error_calback_context_succeeds)
  {
      // arrange
      FRAME_CODEC_HANDLE frame_codec;
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_create());
  
      // act
      frame_codec = frame_codec_create(test_frame_codec_decode_error, NULL);
  
      // assert
      ASSERT_IS_NOT_NULL(frame_codec);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_022: [If allocating memory for the frame_codec instance fails, frame_codec_create shall return NULL.] */
  TEST_FUNCTION(when_allocating_memory_for_the_frame_codec_fails_frame_code_create_fails)
  {
      // arrange
      FRAME_CODEC_HANDLE frame_codec;
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG))
          .SetReturn(NULL);
  
      // act
      frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
  
      // assert
      ASSERT_IS_NULL(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_082: [The initial max_frame_size_shall be 512.] */
  /* Tests_SRS_FRAME_CODEC_01_095: [If the frame_size needed for the frame is bigger than the maximum frame size, frame_codec_encode_frame shall fail and return a non-zero value.] */
  TEST_FUNCTION(sending_a_frame_with_more_than_512_bytes_of_total_frame_size_fails_immediately_after_create)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char bytes[505] = { 0 };
      PAYLOAD payload;
      payload.bytes = bytes;
      payload.length = sizeof(bytes);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, &payload, 1, NULL, 0, NULL, NULL);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_082: [The initial max_frame_size_shall be 512.] */
  /* Tests_SRS_FRAME_CODEC_01_075: [frame_codec_set_max_frame_size shall set the maximum frame size for a frame_codec.] */
  /* Tests_SRS_FRAME_CODEC_01_088: [Encoded bytes shall be passed to the `on_bytes_encoded` callback in a single call, while setting the `encode complete` argument to true.] */
  /* Tests_SRS_FRAME_CODEC_01_108: [ Memory shall be allocated to hold the entire frame. ]*/
  TEST_FUNCTION(a_frame_of_exactly_max_frame_size_immediately_after_create_can_be_sent)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char bytes[504] = { 0 };
      unsigned char expected_bytes[512] = { 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00 };
      PAYLOAD payload;
      payload.bytes = bytes;
      payload.length = sizeof(bytes);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, &payload, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      stringify_bytes(expected_bytes, sizeof(expected_bytes), expected_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, expected_stringified_io, actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_082: [The initial max_frame_size_shall be 512.] */
  /* Tests_SRS_FRAME_CODEC_01_096: [If a frame bigger than the current max frame size is received, frame_codec_receive_bytes shall fail and return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(receiving_a_frame_with_more_than_512_bytes_of_total_frame_size_immediately_after_create_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x02, 0x01 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_082: [The initial max_frame_size_shall be 512.] */
  /* Tests_SRS_FRAME_CODEC_01_096: [If a frame bigger than the current max frame size is received, frame_codec_receive_bytes shall fail and return a non-zero value.] */
  TEST_FUNCTION(receiving_a_frame_with_exactly_512_bytes_of_total_frame_size_immediately_after_create_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[512] = { 0x00, 0x00, 0x02, 0x00, 0x02, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
      (void)memset(frame + 6, 0, 506);
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 504))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[8], 504);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* frame_codec_destroy */
  
  /* Tests_SRS_FRAME_CODEC_01_023: [frame_codec_destroy shall free all resources associated with a frame_codec instance.] */
  TEST_FUNCTION(frame_codec_destroy_frees_the_memory_for_frame_codec)
  {
      // arrange
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_destroy(TEST_LIST_HANDLE));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      frame_codec_destroy(frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  }
  
  /* Tests_SRS_FRAME_CODEC_01_024: [If frame_codec is NULL, frame_codec_destroy shall do nothing.] */
  TEST_FUNCTION(when_frame_codec_is_NULL_frame_codec_destroy_does_nothing)
  {
      // arrange
  
      // act
      frame_codec_destroy(NULL);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  }
  
  /* Tests_SRS_FRAME_CODEC_01_023: [frame_codec_destroy shall free all resources associated with a frame_codec instance.] */
  TEST_FUNCTION(frame_codec_destroy_while_receiving_type_specific_data_frees_the_type_specific_buffer)
  {
      // arrange
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00 };
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
      (void)frame_codec_unsubscribe(frame_codec, 0);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_destroy(TEST_LIST_HANDLE));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      frame_codec_destroy(frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  }
  
  /* frame_codec_set_max_frame_size */
  
  /* Tests_SRS_FRAME_CODEC_01_075: [frame_codec_set_max_frame_size shall set the maximum frame size for a frame_codec.] */
  /* Tests_SRS_FRAME_CODEC_01_076: [On success, frame_codec_set_max_frame_size shall return 0.] */
  TEST_FUNCTION(frame_codec_set_max_frame_size_with_8_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_set_max_frame_size(frame_codec, 8);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  #if 0
  /* Tests_SRS_FRAME_CODEC_01_075: [frame_codec_set_max_frame_size shall set the maximum frame size for a frame_codec.] */
  TEST_FUNCTION(when_a_frame_bigger_than_max_frame_size_is_sent_frame_codec_encode_frame_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_set_max_frame_size(frame_codec, 1024);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, 1017, NULL, 0, NULL, NULL);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_075: [frame_codec_set_max_frame_size shall set the maximum frame size for a frame_codec.] */
  TEST_FUNCTION(a_frame_of_exactly_max_frame_size_can_be_sent)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_set_max_frame_size(frame_codec, 1024);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(xio_send(TEST_IO_HANDLE, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ExpectedAtLeastTimes(1);
      STRICT_EXPECTED_CALL(xio_send(TEST_IO_HANDLE, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .IgnoreAllCalls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, 1016, NULL, 0, NULL, NULL);
  
      // assert
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x00]", actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  #endif
  
  /* Tests_SRS_FRAME_CODEC_01_096: [If a frame bigger than the current max frame size is received, frame_codec_receive_bytes shall fail and return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(receiving_a_frame_with_more_than_max_frame_size_bytes_of_total_frame_size_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x04, 0x01 };
      (void)frame_codec_set_max_frame_size(frame_codec, 1024);
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_096: [If a frame bigger than the current max frame size is received, frame_codec_receive_bytes shall fail and return a non-zero value.] */
  TEST_FUNCTION(receiving_a_frame_with_exactly_max_frame_size_bytes_of_total_frame_size_fails_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[1024] = { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_set_max_frame_size(frame_codec, 1024);
      umock_c_reset_all_calls();
      (void)memset(frame + 6, 0, 1016);
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 1016))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[8], 1016);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_077: [If frame_codec is NULL, frame_codec_set_max_frame_size shall return a non-zero value.] */
  TEST_FUNCTION(when_frame_codec_is_NULL_frame_codec_set_max_frame_size_fails)
  {
      // arrange
  
      // act
      int result = frame_codec_set_max_frame_size(NULL, 1024);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_078: [If max_frame_size is invalid according to the AMQP standard, frame_codec_set_max_frame_size shall return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_010: [The frame is malformed if the size is less than the size of the frame header (8 bytes).] */
  TEST_FUNCTION(when_frame_codec_is_too_small_then_frame_codec_set_max_frame_size_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_set_max_frame_size(frame_codec, 7);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_081: [If a frame being decoded already has a size bigger than the max_frame_size argument then frame_codec_set_max_frame_size shall return a non-zero value and the previous frame size shall be kept.] */
  TEST_FUNCTION(attempting_to_set_a_max_frame_size_lower_than_the_size_of_the_currently_being_received_frame_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[1024] = { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00 };
      (void)frame_codec_set_max_frame_size(frame_codec, 1024);
      (void)memset(frame + 6, 0, 1016);
  
      (void)frame_codec_receive_bytes(frame_codec, frame, 4);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_set_max_frame_size(frame_codec, 8);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_097: [Setting a frame size on a frame_codec that had a decode error shall fail.] */
  TEST_FUNCTION(setting_the_max_frame_size_on_a_codec_with_a_decode_error_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x07 };
  
      (void)frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_set_max_frame_size(frame_codec, 1024);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  #if 0
  /* Tests_SRS_FRAME_CODEC_01_097: [Setting a frame size on a frame_codec that had a decode error shall fail.] */
  TEST_FUNCTION(setting_the_max_frame_size_on_a_codec_with_an_encode_error_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char bytes[] = { 0x42, 0x43 };
      (void)frame_codec_encode_frame(frame_codec, 0x42, sizeof(bytes), NULL, 0, NULL, NULL);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(xio_send(TEST_IO_HANDLE, IGNORED_PTR_ARG, sizeof(bytes), IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(2, &bytes, sizeof(bytes))
          .SetReturn(1);
  
      (void)frame_codec_encode_frame_bytes(frame_codec, bytes, sizeof(bytes));
  
      // act
      result = frame_codec_set_max_frame_size(frame_codec, 1024);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  #endif
  
  /* Tests_SRS_FRAME_CODEC_01_079: [The new frame size shall take effect immediately, even for a frame that is being decoded at the time of the call.] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(setting_a_new_max_frame_while_the_frame_size_is_being_received_makes_the_new_frame_size_be_in_effect)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00 };
  
      (void)frame_codec_receive_bytes(frame_codec, frame, 3);
      umock_c_reset_all_calls();
  
      (void)frame_codec_set_max_frame_size(frame_codec, 8);
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame + 3, sizeof(frame) - 3);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* frame_codec_receive_bytes */
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  /* Tests_SRS_FRAME_CODEC_01_028: [The sequence of bytes shall be decoded according to the AMQP ISO.] */
  /* Tests_SRS_FRAME_CODEC_01_031: [When a complete frame is successfully decoded it shall be indicated to the upper layer by invoking the on_frame_received passed to frame_codec_subscribe.] */
  /* Tests_SRS_FRAME_CODEC_01_032: [Besides passing the frame information, the callback_context value passed to frame_codec_subscribe shall be passed to the on_frame_received_1 function.] */
  /* Tests_SRS_FRAME_CODEC_01_001: [Frames are divided into three distinct areas: a fixed width frame header, a variable width extended header, and a variable width frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_002: [frame header The frame header is a fixed size (8 byte) structure that precedes each frame.] */
  /* Tests_SRS_FRAME_CODEC_01_003: [The frame header includes mandatory information necessary to parse the rest of the frame including size and type information.] */
  /* Tests_SRS_FRAME_CODEC_01_004: [extended header The extended header is a variable width area preceding the frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_007: [frame body The frame body is a variable width sequence of bytes the format of which depends on the frame type.] */
  /* Tests_SRS_FRAME_CODEC_01_008: [SIZE Bytes 0-3 of the frame header contain the frame size.] */
  /* Tests_SRS_FRAME_CODEC_01_009: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_011: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_012: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_013: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */
  /* Tests_SRS_FRAME_CODEC_01_015: [TYPE Byte 5 of the frame header is a type code.] */
  /* Tests_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value. */
  /* Tests_SRS_FRAME_CODEC_01_100: [If the frame body size is 0, the frame_body pointer passed to on_frame_received shall be NULL.] */
  TEST_FUNCTION(frame_codec_receive_bytes_decodes_one_empty_frame)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  /* Tests_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */
  TEST_FUNCTION(frame_codec_receive_bytes_with_not_enough_bytes_for_a_frame_does_not_trigger_callback)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_026: [If frame_codec or buffer are NULL, frame_codec_receive_bytes shall return a non-zero value.] */
  TEST_FUNCTION(frame_codec_receive_bytes_with_NULL_frame_codec_handle_fails)
  {
      // arrange
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
  
      // act
      int result = frame_codec_receive_bytes(NULL, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_026: [If frame_codec or buffer are NULL, frame_codec_receive_bytes shall return a non-zero value.] */
  TEST_FUNCTION(frame_codec_receive_bytes_with_NULL_buffer_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_receive_bytes(frame_codec, NULL, 1);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_027: [If size is zero, frame_codec_receive_bytes shall return a non-zero value.] */
  TEST_FUNCTION(frame_codec_receive_bytes_with_zero_size_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, 0);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */
  /* Codes_SRS_FRAME_CODEC_01_005: [This is an extension point defined for future expansion.] */
  /* Codes_SRS_FRAME_CODEC_01_006: [The treatment of this area depends on the frame type.] */
  TEST_FUNCTION(when_frame_codec_receive_1_byte_in_one_call_and_the_rest_of_the_frame_in_another_call_yields_succesfull_decode)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      (void)frame_codec_receive_bytes(frame_codec, frame, 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame + 1, sizeof(frame) - 1);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */
  TEST_FUNCTION(when_frame_codec_receive_the_frame_bytes_in_1_byte_per_call_a_succesfull_decode_happens)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      size_t i;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      for (i = 0; i < sizeof(frame) - 1; i++)
      {
          (void)frame_codec_receive_bytes(frame_codec, &frame[i], 1);
      }
  
      // act
      result = frame_codec_receive_bytes(frame_codec, &frame[sizeof(frame) - 1], 1);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */
  TEST_FUNCTION(a_frame_codec_receive_bytes_call_with_bad_args_before_any_real_frame_bytes_does_not_affect_decoding)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      (void)frame_codec_receive_bytes(frame_codec, NULL, 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_029: [The sequence of bytes does not have to be a complete frame, frame_codec shall be responsible for maintaining decoding state between frame_codec_receive_bytes calls.] */
  TEST_FUNCTION(a_frame_codec_receive_bytes_call_with_bad_args_in_the_middle_of_the_frame_does_not_affect_decoding)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      (void)frame_codec_receive_bytes(frame_codec, frame, 1);
      (void)frame_codec_receive_bytes(frame_codec, NULL, 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame + 1, sizeof(frame) - 1);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  TEST_FUNCTION(frame_codec_receive_bytes_decodes_2_empty_frames)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame1[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02 };
      unsigned char frame2[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x03, 0x04 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame1[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame1[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame2[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame2[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      (void)frame_codec_receive_bytes(frame_codec, frame1, sizeof(frame1));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame2, sizeof(frame2));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  TEST_FUNCTION(a_call_to_frame_codec_receive_bytes_with_bad_args_between_2_frames_does_not_affect_decoding)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame1[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02 };
      unsigned char frame2[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x03, 0x04 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame1[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame1[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame2[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame2[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      (void)frame_codec_receive_bytes(frame_codec, frame1, sizeof(frame1));
      (void)frame_codec_receive_bytes(frame_codec, NULL, 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame2, sizeof(frame2));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  TEST_FUNCTION(when_getting_the_list_item_value_fails_no_callback_is_invoked)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG))
          .SetReturn(NULL);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_010: [The frame is malformed if the size is less than the size of the frame header (8 bytes).] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(when_frame_size_is_bad_frame_codec_receive_bytes_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x01, 0x02 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_014: [Due to the mandatory 8-byte frame header, the frame is malformed if the value is less than 2.] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(when_frame_size_has_a_bad_doff_frame_codec_receive_bytes_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x02 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_receive_bytes shall fail.] */
  TEST_FUNCTION(after_a_frame_decode_error_occurs_due_to_frame_size_a_subsequent_decode_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char bad_frame[] = { 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x01, 0x02 };
      unsigned char good_frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      (void)frame_codec_receive_bytes(frame_codec, bad_frame, sizeof(bad_frame));
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_receive_bytes(frame_codec, good_frame, sizeof(good_frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_receive_bytes shall fail.] */
  TEST_FUNCTION(after_a_frame_decode_error_occurs_due_to_bad_doff_size_a_subsequent_decode_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char bad_frame[] = { 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x01, 0x02 };
      unsigned char good_frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      (void)frame_codec_receive_bytes(frame_codec, bad_frame, sizeof(bad_frame));
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_receive_bytes(frame_codec, good_frame, sizeof(good_frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.] */
  /* Tests_SRS_FRAME_CODEC_01_031: [When a complete frame is successfully decoded it shall be indicated to the upper layer by invoking the on_frame_received passed to frame_codec_subscribe.] */
  /* Tests_SRS_FRAME_CODEC_01_099: [A pointer to the frame_body bytes shall also be passed to the on_frame_received.] */
  /* Tests_SRS_FRAME_CODEC_01_102: [frame_codec_receive_bytes shall allocate memory to hold the frame_body bytes.] */
  TEST_FUNCTION(receiving_a_frame_with_1_byte_frame_body_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x01, 0x02, 0x42 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 1))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[8], 1);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_101: [If the memory for the frame_body bytes cannot be allocated, frame_codec_receive_bytes shall fail and return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_103: [Upon any decode error, if an error callback has been passed to frame_codec_create, then the error callback shall be called with the context argument being the frame_codec_error_callback_context argument passed to frame_codec_create.] */
  TEST_FUNCTION(when_allocating_type_specific_data_fails_frame_codec_receive_bytes_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x01, 0x02, 0x42 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG))
          .SetReturn(NULL);
  
      STRICT_EXPECTED_CALL(test_frame_codec_decode_error(TEST_ERROR_CONTEXT));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_030: [If a decoding error occurs, frame_codec_receive_bytes shall return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_074: [If a decoding error is detected, any subsequent calls on frame_codec_data_receive_bytes shall fail.] */
  TEST_FUNCTION(when_allocating_type_specific_data_fails_a_subsequent_decode_Call_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x01, 0x02, 0x42 };
      frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG))
          .SetReturn(NULL);
  
      (void)frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_031: [When a complete frame is successfully decoded it shall be indicated to the upper layer by invoking the on_frame_received passed to frame_codec_subscribe.] */
  TEST_FUNCTION(a_frame_with_2_bytes_received_together_with_the_header_passes_the_bytes_in_one_call)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x01, 0x02, 0x42, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 2))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[sizeof(frame) - 2], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.]  */
  TEST_FUNCTION(two_empty_frames_received_in_the_same_call_yields_2_callbacks)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x01, 0x02,
          0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x03, 0x04 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[6], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 0))
          .ValidateArgumentBuffer(2, &frame[14], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_025: [frame_codec_receive_bytes decodes a sequence of bytes into frames and on success it shall return zero.]  */
  TEST_FUNCTION(two_frames_with_1_byte_each_received_in_the_same_call_yields_2_callbacks)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x01, 0x02, 0x42,
          0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x03, 0x04, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 1))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[8], 1);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 1))
          .ValidateArgumentBuffer(2, &frame[15], 2)
          .ValidateArgumentBuffer(4, &frame[17], 1);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* frame_codec_subscribe */
  
  /* Tests_SRS_FRAME_CODEC_01_033: [frame_codec_subscribe subscribes for a certain type of frame received by the frame_codec instance identified by frame_codec.] */
  /* Tests_SRS_FRAME_CODEC_01_087: [On success, frame_codec_subscribe shall return zero.] */
  TEST_FUNCTION(frame_codec_subscribe_with_valid_args_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_add(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_033: [frame_codec_subscribe subscribes for a certain type of frame received by the frame_codec instance identified by frame_codec.] */
  /* Tests_SRS_FRAME_CODEC_01_087: [On success, frame_codec_subscribe shall return zero.] */
  TEST_FUNCTION(when_list_find_returns_NULL_a_new_subscription_is_created)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1)
          .SetReturn(NULL);
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_add(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_list_item_get_value_returns_NULL_subscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG))
          .SetReturn(NULL);
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_034: [If any of the frame_codec or on_frame_received arguments is NULL, frame_codec_subscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_frame_codec_is_NULL_frame_codec_subscribe_fails)
  {
      // arrange
  
      // act
      int result = frame_codec_subscribe(NULL, 0, on_frame_received_1, (void*)0x01);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_034: [If any of the frame_codec or on_frame_received arguments is NULL, frame_codec_subscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_on_frame_received_is_NULL_frame_codec_subscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, NULL, frame_codec);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value. */
  TEST_FUNCTION(when_a_frame_type_that_has_no_subscribers_is_received_no_callback_is_called)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x01, 0x00, 0x00 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value.] */
  TEST_FUNCTION(when_no_subscribe_is_done_no_callback_is_called)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x08, 0x02, 0x01, 0x00, 0x00 };
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value.] */
  TEST_FUNCTION(when_2_subscriptions_exist_and_first_one_matches_the_callback_is_invoked)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x01, 0x02, 0x42, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_subscribe(frame_codec, 1, on_frame_received_2, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_1(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 2))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[sizeof(frame) - 2], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      (void)frame_codec_unsubscribe(frame_codec, 1);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_035: [After successfully registering a callback for a certain frame type, when subsequently that frame type is received the callbacks shall be invoked, passing to it the received frame and the callback_context value.] */
  TEST_FUNCTION(when_2_subscriptions_exist_and_second_one_matches_the_callback_is_invoked)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x0A, 0x02, 0x01, 0x01, 0x02, 0x42, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_subscribe(frame_codec, 1, on_frame_received_2, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_2(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 2))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[sizeof(frame) - 2], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      (void)frame_codec_unsubscribe(frame_codec, 1);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_036: [Only one callback pair shall be allowed to be registered for a given frame type.] */
  TEST_FUNCTION(when_frame_codec_subscribe_is_called_twice_for_the_same_frame_type_it_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_2, frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_036: [Only one callback pair shall be allowed to be registered for a given frame type.] */
  TEST_FUNCTION(the_callbacks_for_the_2nd_frame_codec_subscribe_for_the_same_frame_type_remain_in_effect)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x01, 0x02, 0x42, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_2, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(on_frame_received_2(frame_codec, IGNORED_PTR_ARG, 2, IGNORED_PTR_ARG, 2))
          .ValidateArgumentBuffer(2, &frame[6], 2)
          .ValidateArgumentBuffer(4, &frame[sizeof(frame) - 2], 2);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_allocating_memory_for_the_subscription_fails_frame_codec_subscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG))
          .SetReturn(NULL);
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_037: [If any failure occurs while performing the subscribe operation, frame_codec_subscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_adding_the_subscription_fails_then_frame_codec_subscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_add(TEST_LIST_HANDLE, IGNORED_PTR_ARG))
          .SetReturn(NULL);
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* frame_codec_unsubscribe */
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(removing_an_existing_subscription_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_remove(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 0);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(removing_an_existing_subscription_does_not_trigger_callback_when_a_frame_of_that_type_is_received)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      unsigned char frame[] = { 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x01, 0x02, 0x42, 0x43 };
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_unsubscribe(frame_codec, 0);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame[5], 1);
  
      // act
      result = frame_codec_receive_bytes(frame_codec, frame, sizeof(frame));
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_039: [If frame_codec is NULL, frame_codec_unsubscribe shall return a non-zero value.] */
  TEST_FUNCTION(frame_codec_unsubscribe_with_NULL_frame_codec_handle_fails)
  {
      // arrange
  
      // act
      int result = frame_codec_unsubscribe(NULL, 0);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_040: [If no subscription for the type frame type exists, frame_codec_unsubscribe shall return a non-zero value.] */
  TEST_FUNCTION(frame_codec_unsubscribe_with_no_subscribe_call_has_been_made_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 0);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_041: [If any failure occurs while performing the unsubscribe operation, frame_codec_unsubscribe shall return a non-zero value.] */
  TEST_FUNCTION(when_list_remove_matching_item_fails_then_frame_codec_unsubscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1)
          .SetReturn(NULL);
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 0);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(unsubscribe_one_of_2_subscriptions_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_subscribe(frame_codec, 1, on_frame_received_2, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_remove(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 0);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 1);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(unsubscribe_2nd_out_of_2_subscriptions_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 1;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_subscribe(frame_codec, 1, on_frame_received_2, frame_codec);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_item_get_value(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_remove(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 1);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(subscribe_unsubscribe_subscribe_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_unsubscribe(frame_codec, 0);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
      STRICT_EXPECTED_CALL(gballoc_calloc(IGNORED_NUM_ARG, IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(singlylinkedlist_add(TEST_LIST_HANDLE, IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      (void)frame_codec_unsubscribe(frame_codec, 0);
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_038: [frame_codec_unsubscribe removes a previous subscription for frames of type type and on success it shall return 0.] */
  TEST_FUNCTION(subscribe_unsubscribe_unsubscribe_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      uint8_t frame_type = 0;
      (void)frame_codec_subscribe(frame_codec, 0, on_frame_received_1, frame_codec);
      (void)frame_codec_unsubscribe(frame_codec, 0);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(singlylinkedlist_find(TEST_LIST_HANDLE, IGNORED_PTR_ARG, IGNORED_PTR_ARG))
          .ValidateArgumentBuffer(3, &frame_type, 1);
  
      // act
      result = frame_codec_unsubscribe(frame_codec, 0);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_016: [The type code indicates the format and purpose of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_017: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_018: [A type code of 0x00 indicates that the frame is an AMQP frame.] */
  /* Tests_SRS_FRAME_CODEC_01_070: [The type code indicates the format and purpose of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_071: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_072: [A type code of 0x00 indicates that the frame is an AMQP frame.] */
  TEST_FUNCTION(frame_type_amqp_is_zero)
  {
      // arrange
  
      // act
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_EQUAL(uint8_t, 0, FRAME_TYPE_AMQP);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_016: [The type code indicates the format and purpose of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_017: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_019: [A type code of 0x01 indicates that the frame is a SASL frame] */
  /* Tests_SRS_FRAME_CODEC_01_070: [The type code indicates the format and purpose of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_071: [The subsequent bytes in the frame header MAY be interpreted differently depending on the type of the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_073: [A type code of 0x01 indicates that the frame is a SASL frame] */
  TEST_FUNCTION(frame_type_sasl_is_one)
  {
      // arrange
  
      // act
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_EQUAL(uint8_t, 1, FRAME_TYPE_SASL);
  }
  
  /* frame_codec_encode_frame */
  
  /* Tests_SRS_FRAME_CODEC_01_042: [frame_codec_encode_frame encodes the header, type specific bytes and frame payload of a frame that has frame_payload_size bytes.]*/
  /* Tests_SRS_FRAME_CODEC_01_043: [On success it shall return 0.] */
  /* Tests_SRS_FRAME_CODEC_01_088: [Encoded bytes shall be passed to the `on_bytes_encoded` callback in a single call, while setting the `encode complete` argument to true.] */
  /* Tests_SRS_FRAME_CODEC_01_055: [Frames are divided into three distinct areas: a fixed width frame header, a variable width extended header, and a variable width frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_056: [frame header The frame header is a fixed size (8 byte) structure that precedes each frame.] */
  /* Tests_SRS_FRAME_CODEC_01_057: [The frame header includes mandatory information necessary to parse the rest of the frame including size and type information.] */
  /* Tests_SRS_FRAME_CODEC_01_058: [extended header The extended header is a variable width area preceding the frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_059: [This is an extension point defined for future expansion.] */
  /* Tests_SRS_FRAME_CODEC_01_060: [The treatment of this area depends on the frame type.] */
  /* Tests_SRS_FRAME_CODEC_01_062: [SIZE Bytes 0-3 of the frame header contain the frame size.] */
  /* Tests_SRS_FRAME_CODEC_01_063: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_064: [The frame is malformed if the size is less than the size of the frame header (8 bytes).] */
  TEST_FUNCTION(frame_codec_encode_frame_with_a_zero_frame_body_length_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x00,0x08,0x02,0x00,0x00,0x00]", actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_044: [If any of arguments `frame_codec` or `on_bytes_encoded` is NULL, `frame_codec_encode_frame` shall return a non-zero value.] */
  TEST_FUNCTION(when_frame_codec_is_NULL_frame_codec_encode_frame_fails)
  {
      // arrange
  
      // act
      int result = frame_codec_encode_frame(NULL, 0, NULL, 0, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_044: [If any of arguments `frame_codec` or `on_bytes_encoded` is NULL, `frame_codec_encode_frame` shall return a non-zero value.] */
  TEST_FUNCTION(when_on_bytes_encoded_is_NULL_frame_codec_encode_frame_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, NULL, 0, NULL, (void*)0x4242);
  
      // assert
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_091: [If the argument type_specific_size is greater than 0 and type_specific_bytes is NULL, frame_codec_encode_frame shall return a non-zero value.] */
  TEST_FUNCTION(when_type_specific_size_is_positive_and_type_specific_bytes_is_NULL_frame_codec_encode_frame_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, NULL, 1, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_092: [If type_specific_size is too big to allow encoding the frame according to the AMQP ISO then frame_codec_encode_frame shall return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_066: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_058: [extended header The extended header is a variable width area preceding the frame body.] */
  TEST_FUNCTION(when_type_specific_size_is_too_big_then_frame_codec_encode_frame_fails)
  {
      // arrange
      int result;
      unsigned char expected_frame[1020] = { 0x00, 0x00, 0x00, 0x0A, 0xFF, 0x00, 0x00, 0x00 };
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_set_max_frame_size(frame_codec, 4096);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, &expected_frame[6], 1015, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_092: [If type_specific_size is too big to allow encoding the frame according to the AMQP ISO then frame_codec_encode_frame shall return a non-zero value.] */
  /* Tests_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_066: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_058: [extended header The extended header is a variable width area preceding the frame body.] */
  /* Tests_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_066: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_067: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */
  /* Tests_SRS_FRAME_CODEC_01_063: [This is an unsigned 32-bit integer that MUST contain the total frame size of the frame header, extended header, and frame body.] */
  TEST_FUNCTION(when_type_specific_size_is_max_allowed_then_frame_codec_encode_frame_succeeds)
  {
      // arrange
      int result;
      unsigned char expected_frame[1020] = { 0x00, 0x00, 0x03, 0xFC, 0xFF, 0x00, 0x00, 0x00 };
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      (void)frame_codec_set_max_frame_size(frame_codec, 4096);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, &expected_frame[6], 1014, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      memset(expected_frame + 6, 0, 1020 - 6);
      stringify_bytes(expected_frame, sizeof(expected_frame), expected_stringified_io);
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, expected_stringified_io, actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_090: [If the type_specific_size - 2 does not divide by 4, frame_codec_encode_frame shall pad the type_specific bytes with zeroes so that type specific data is according to the AMQP ISO.] */
  /* Tests_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_066: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_067: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */
  /* Tests_SRS_FRAME_CODEC_01_068: [Due to the mandatory 8-byte frame header, the frame is malformed if the value is less than 2.] */
  TEST_FUNCTION(one_byte_of_padding_is_added_to_type_specific_data_to_make_the_frame_header)
  {
      // arrange
      int result;
      unsigned char expected_frame[] = { 0x00, 0x00, 0x00, 0x8, 0x02, 0x00, 0x42, 0x00 };
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, &expected_frame[6], 1, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(expected_frame, sizeof(expected_frame), expected_stringified_io);
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, expected_stringified_io, actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_090: [If the type_specific_size - 2 does not divide by 4, frame_codec_encode_frame shall pad the type_specific bytes with zeroes so that type specific data is according to the AMQP ISO.] */
  /* Tests_SRS_FRAME_CODEC_01_065: [DOFF Byte 4 of the frame header is the data offset.] */
  /* Tests_SRS_FRAME_CODEC_01_066: [This gives the position of the body within the frame.] */
  /* Tests_SRS_FRAME_CODEC_01_067: [The value of the data offset is an unsigned, 8-bit integer specifying a count of 4-byte words.] */
  /* Tests_SRS_FRAME_CODEC_01_068: [Due to the mandatory 8-byte frame header, the frame is malformed if the value is less than 2.] */
  TEST_FUNCTION(no_bytes_of_padding_are_added_to_type_specific_data_when_enough_bytes_are_there)
  {
      // arrange
      int result;
      unsigned char expected_frame[] = { 0x00, 0x00, 0x00, 0x8, 0x02, 0x00, 0x42, 0x00 };
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, &expected_frame[6], 2, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(expected_frame, sizeof(expected_frame), expected_stringified_io);
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, expected_stringified_io, actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_069: [TYPE Byte 5 of the frame header is a type code.] */
  TEST_FUNCTION(the_type_is_placed_in_the_underlying_frame)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, NULL, 0, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x00,0x08,0x02,0x42,0x00,0x00]", actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_105: [The frame_payload_size shall be computed by summing up the lengths of the payload segments identified by the payloads argument.]*/
  /* ----------- Tests_SRS_FRAME_CODEC_01_048: [If all bytes are successfully encoded, frame_codec_encode_frame_bytes shall return 0.] */
  /* Tests_SRS_FRAME_CODEC_01_061: [frame body The frame body is a variable width sequence of bytes the format of which depends on the frame type.] */
  TEST_FUNCTION(frame_codec_encode_frame_bytes_with_1_encoded_byte_succeeds)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      PAYLOAD payloads[1];
      uint8_t byte = 0x42;
      umock_c_reset_all_calls();
      payloads[0].bytes = &byte;
      payloads[0].length = 1;
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x00,0x09,0x02,0x42,0x00,0x00,0x42]", actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* ----------- Tests_SRS_FRAME_CODEC_01_047: [frame_codec_encode_frame_bytes encodes the frame bytes for a frame encoding started with a frame_codec_start_encode_frame call.] */
  /* ----------- Tests_SRS_FRAME_CODEC_01_048: [If all bytes are successfully encoded, frame_codec_encode_frame_bytes shall return 0.] */
  TEST_FUNCTION(frame_codec_encode_frame_bytes_with_2_bytes_succeeds)
  {
      // arrange
      int result;
      unsigned char bytes[] = { 0x42, 0x43 };
      PAYLOAD payloads[1];
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      payloads[0].bytes = bytes;
      payloads[0].length = sizeof(bytes);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_110: [ If the `bytes` member of a payload entry is NULL, `frame_codec_encode_frame` shall fail and return a non-zero value. ] */
  TEST_FUNCTION(frame_codec_encode_frame_bytes_with_NULL_bytes_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      PAYLOAD payloads[1];
      payloads[0].bytes = NULL;
      payloads[0].length = 1;
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_111: [ If the `length` member of a payload entry is 0, `frame_codec_encode_frame` shall fail and return a non-zero value. ] */
  TEST_FUNCTION(frame_codec_encode_frame_bytes_with_zero_length_fails)
  {
      // arrange
      int result;
      unsigned char bytes[] = { 0x42, 0x43 };
      PAYLOAD payloads[1];
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      payloads[0].bytes = bytes;
      payloads[0].length = 0;
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_105: [The frame_payload_size shall be computed by summing up the lengths of the payload segments identified by the payloads argument.] */
  /* Tests_SRS_FRAME_CODEC_01_106: [All payloads shall be encoded in order as part of the frame.] */
  TEST_FUNCTION(sending_only_1_byte_out_of_2_frame_body_bytes_succeeds)
  {
      // arrange
      int result;
      unsigned char bytes[] = { 0x42, 0x43 };
      PAYLOAD payloads[2];
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      payloads[0].bytes = bytes;
      payloads[0].length = 1;
      payloads[1].bytes = bytes + 1;
      payloads[1].length = 1;
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 2, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      ASSERT_ARE_EQUAL(int, 0, result);
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x00,0x0A,0x02,0x42,0x00,0x00,0x42,0x43]", actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_105: [The frame_payload_size shall be computed by summing up the lengths of the payload segments identified by the payloads argument.] */
  /* Tests_SRS_FRAME_CODEC_01_106: [All payloads shall be encoded in order as part of the frame.] */
  TEST_FUNCTION(a_send_after_send_succeeds)
  {
      // arrange
      int result;
      unsigned char bytes[] = { 0x42, 0x43 };
      PAYLOAD payloads[1];
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      payloads[0].bytes = bytes;
      payloads[0].length = 2;
      (void)frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
      STRICT_EXPECTED_CALL(test_on_bytes_encoded((void*)0x4242, IGNORED_PTR_ARG, IGNORED_NUM_ARG, true));
      STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0x42, payloads, 1, NULL, 0, test_on_bytes_encoded, (void*)0x4242);
  
      // assert
      stringify_bytes(sent_io_bytes, sent_io_byte_count, actual_stringified_io);
      ASSERT_ARE_EQUAL(char_ptr, "[0x00,0x00,0x00,0x0A,0x02,0x42,0x00,0x00,0x42,0x43,0x00,0x00,0x00,0x0A,0x02,0x42,0x00,0x00,0x42,0x43]", actual_stringified_io);
      ASSERT_ARE_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_109: [ If allocating memory fails, `frame_codec_encode_frame` shall fail and return a non-zero value. ]*/
  TEST_FUNCTION(when_allocating_memory_for_the_encoded_frame_fails_frame_codec_encode_frame_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG))
          .SetReturn(NULL);
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 0, NULL, 0, test_on_bytes_encoded, NULL);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  /* Tests_SRS_FRAME_CODEC_01_107: [If the argument `payloads` is NULL and `payload_count` is non-zero, `frame_codec_encode_frame` shall return a non-zero value.]*/
  TEST_FUNCTION(frame_codec_encode_frame_with_NULL_payloads_and_non_zero_payload_count_fails)
  {
      // arrange
      int result;
      FRAME_CODEC_HANDLE frame_codec = frame_codec_create(test_frame_codec_decode_error, TEST_ERROR_CONTEXT);
      umock_c_reset_all_calls();
  
      // act
      result = frame_codec_encode_frame(frame_codec, 0, NULL, 1, NULL, 0, test_on_bytes_encoded, NULL);
  
      // assert
      ASSERT_ARE_NOT_EQUAL(int, 0, result);
      ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
  
      // cleanup
      frame_codec_destroy(frame_codec);
  }
  
  END_TEST_SUITE(frame_codec_ut)
  