// 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 <cstdlib>
#include <cstddef>
#include <cstdint>
#include <cinttypes>
#else
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#endif

void* my_gballoc_malloc(size_t t)
{
    return malloc(t);
}

void my_gballoc_free(void * t)
{
    free(t);
}

#include "azure_macro_utils/macro_utils.h"
#include "umock_c/umock_c.h"
#include "umock_c/umocktypes_charptr.h"
#include "umock_c/umocktypes_bool.h"
#include "umock_c/umocktypes_stdint.h"
#include "umock_c/umock_c_negative_tests.h"

#define ENABLE_MOCKS
#include "agenttypesystem.h"
#include "schema.h"
#include "iotdevice.h"
#undef ENABLE_MOCKS

#include "testrunnerswitcher.h"
#include "codefirst.h"
#include "azure_macro_utils/macro_utils.h"

#include "serializer.h"

BEGIN_NAMESPACE(DummyDataProvider_WithStructs)

DECLARE_STRUCT(GeoLocation,
double, Lat,
double, Long);

DECLARE_STRUCT(CarLocation,
int, Alt,
GeoLocation, whereIsMyCar);

DECLARE_MODEL(TruckType,
WITH_DATA(double, this_is_double),
WITH_DATA(GeoLocation, whereIsMyDevice),
WITH_DATA(CarLocation, theCarIsBehindTheVan),

/*a simple action that takes as parameter a struct*/
WITH_ACTION(goToLocation, GeoLocation, XMarksTheSpot),

/*a more complex action that takes as parameter 1xsimple type, 1x complex type (that has another complex type embedded) and another simple type*/
WITH_ACTION(moveCarTo, int, speed, CarLocation, destination, bool, reverse),
WITH_ACTION(alwaysReject),
WITH_ACTION(alwaysAbandon)
);


END_NAMESPACE(DummyDataProvider_WithStructs)

BEGIN_NAMESPACE(TheSecondDummyDataProvider_WithStructs)

DECLARE_STRUCT(FamilyAccount,
int16_t, FatherMoney,
int8_t, MotherMoney);

DECLARE_STRUCT(MoneyInTheWorld,
FamilyAccount, moneyInCanada,
FamilyAccount, moneyInRomania,
FamilyAccount, moneyInUSA);

DECLARE_MODEL(someModel,
WITH_DATA(int8_t, myAccount),
WITH_DATA(FamilyAccount, UsMoney),
WITH_DATA(MoneyInTheWorld, allTheMoney)
);

END_NAMESPACE(TheSecondDummyDataProvider_WithStructs)

bool goToLocation_wasCalled;
double goToLocation_XMarksTheSpot_long;
double goToLocation_XMarksTheSpot_lat;
EXECUTE_COMMAND_RESULT goToLocation(TruckType* device, GeoLocation XMarksTheSpot)
{
    (void)device;

    goToLocation_wasCalled = true;
    goToLocation_XMarksTheSpot_long = XMarksTheSpot.Long;
    goToLocation_XMarksTheSpot_lat = XMarksTheSpot.Lat;
    return EXECUTE_COMMAND_SUCCESS;

}

bool moveCarTo_wasCalled;
int moveCarTo_speed;
int moveCarTo_destination_Alt;
double moveCarTo_destination_whereIsMyCar_Lat;
double moveCarTo_destination_whereIsMyCar_Long;
bool moveCarTo_reverse;
EXECUTE_COMMAND_RESULT moveCarTo(TruckType* device, int speed, CarLocation destination, bool reverse)
{
    (void)device;
    moveCarTo_wasCalled = true;
    moveCarTo_speed = speed;
    moveCarTo_destination_Alt = destination.Alt;
    moveCarTo_destination_whereIsMyCar_Lat = destination.whereIsMyCar.Lat;
    moveCarTo_destination_whereIsMyCar_Long = destination.whereIsMyCar.Long;
    moveCarTo_reverse = reverse;
    return EXECUTE_COMMAND_SUCCESS;
}

EXECUTE_COMMAND_RESULT alwaysReject(TruckType* device)
{
    (void)(device);
    return EXECUTE_COMMAND_FAILED;
}
EXECUTE_COMMAND_RESULT alwaysAbandon(TruckType* device)
{
    (void)(device);
    return EXECUTE_COMMAND_ERROR;
}

static const DEVICE_HANDLE TEST_DEVICE_HANDLE = (DEVICE_HANDLE)0x4848;
static const SCHEMA_MODEL_TYPE_HANDLE TEST_MODEL_HANDLE = (SCHEMA_MODEL_TYPE_HANDLE)0x4243;
static void* g_InvokeActionCallbackArgument;

#define MAX_RECORDINGS 100
#define MAX_NAME_LENGTH 100
typedef char someName[MAX_NAME_LENGTH];
static  AGENT_DATA_TYPE* Create_EDM_BOOLEAN_from_int_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_UINT8_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_DOUBLE_agentData[MAX_RECORDINGS];
static  double Create_AGENT_DATA_TYPE_from_DOUBLE_values[MAX_RECORDINGS];
static size_t nCreate_AGENT_DATA_TYPE_from_DOUBLE_agentData;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_SINT16_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_SINT32_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_SINT64_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_SINT8_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_FLOAT_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_charz_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_charz_agentData_no_quotes = NULL;
static AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_Members_agentData = NULL;
static someName Create_AGENT_DATA_TYPE_from_Members_names[MAX_RECORDINGS];
static const AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_Members_values[MAX_RECORDINGS];
static const AGENT_DATA_TYPE* Device_Publish_agentData = NULL;
static const AGENT_DATA_TYPE* Device_PublishTransacted_agentData = NULL;
static const AGENT_DATA_TYPE* Destroy_AGENT_DATA_TYPE_agentData[MAX_RECORDINGS];
static size_t nDestroy_AGENT_DATA_TYPE_agentData;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_EDM_GUID_agentData = NULL;
static  AGENT_DATA_TYPE* Create_AGENT_DATA_TYPE_from_EDM_BINARY_agentData = NULL;

static DEVICE_HANDLE serializer_getdevicehandle(void) { return TEST_DEVICE_HANDLE; }

static TEST_MUTEX_HANDLE g_testByTest;

static STRING_HANDLE global_bufferTemp;

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 :%s", MU_ENUM_TO_STRING(UMOCK_C_ERROR_CODE, error_code));
}

TEST_DEFINE_ENUM_TYPE(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES);
TEST_DEFINE_ENUM_TYPE(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_RESULT_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_RESULT_VALUES);


static DEVICE_RESULT my_Device_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, pfDeviceActionCallback deviceActionCallback, void* actionCallbackUserContext, pfDeviceMethodCallback deviceMethodCallback, void* methodCallbackUserContext, bool includePropertyPath, DEVICE_HANDLE* deviceHandle)
{
    (void)modelHandle;
    (void)deviceActionCallback;
    (void)includePropertyPath;
    (void)deviceMethodCallback;
    (void)methodCallbackUserContext;
    *deviceHandle = TEST_DEVICE_HANDLE;
    g_InvokeActionCallbackArgument = actionCallbackUserContext;
    return DEVICE_OK;
}

static SCHEMA_RESULT my_Schema_GetModelDesiredPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* desiredPropertyCount)
{
    (void)modelTypeHandle;
    if (desiredPropertyCount != NULL)
    {
        *desiredPropertyCount = 0;
    }
    return SCHEMA_OK;
}

static SCHEMA_RESULT my_Schema_GetModelModelCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* modelCount)
{
    (void)modelTypeHandle;
    if (modelCount != NULL)
    {
        *modelCount = 0;
    }
    return SCHEMA_OK;
}

BEGIN_TEST_SUITE(CodeFirst_ut_Two_Providers_With_Structs)

TEST_SUITE_INITIALIZE(TestClassInitialize)
{
    g_testByTest = TEST_MUTEX_CREATE();
    ASSERT_IS_NOT_NULL(g_testByTest);

    (void)umock_c_init(on_umock_c_error);

    (void)umocktypes_bool_register_types();
    (void)umocktypes_charptr_register_types();
    (void)umocktypes_stdint_register_types();

    REGISTER_GLOBAL_MOCK_HOOK(Device_Create, my_Device_Create);
    REGISTER_UMOCK_ALIAS_TYPE(SCHEMA_MODEL_TYPE_HANDLE, void*);
    REGISTER_UMOCK_ALIAS_TYPE(pfDeviceActionCallback, void*);
    REGISTER_UMOCK_ALIAS_TYPE(DEVICE_HANDLE, void*);
    REGISTER_UMOCK_ALIAS_TYPE(STRING_HANDLE, void*);
    REGISTER_UMOCK_ALIAS_TYPE(pfDeviceMethodCallback, void*);


    REGISTER_GLOBAL_MOCK_HOOK(Schema_GetModelDesiredPropertyCount, my_Schema_GetModelDesiredPropertyCount);
    REGISTER_GLOBAL_MOCK_HOOK(Schema_GetModelModelCount, my_Schema_GetModelModelCount);

    REGISTER_GLOBAL_MOCK_RETURN(Schema_AddDeviceRef, SCHEMA_OK);

}

TEST_SUITE_CLEANUP(TestClassCleanup)
{
    umock_c_deinit();

    TEST_MUTEX_DESTROY(g_testByTest);
}

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

    umock_c_reset_all_calls();

    nCreate_AGENT_DATA_TYPE_from_DOUBLE_agentData = 0;
    nDestroy_AGENT_DATA_TYPE_agentData = 0;

    goToLocation_wasCalled = false;
    goToLocation_XMarksTheSpot_long = -1.0;
    goToLocation_XMarksTheSpot_lat = -1.0;

    moveCarTo_wasCalled = false;
    moveCarTo_speed = -1;
    moveCarTo_destination_Alt = -1;
    moveCarTo_destination_whereIsMyCar_Lat = -1.0;
    moveCarTo_destination_whereIsMyCar_Long = -1.0;
    moveCarTo_reverse = false;

    global_bufferTemp = STRING_new();

}

TEST_FUNCTION_CLEANUP(TestMethodCleanup)
{
    STRING_delete(global_bufferTemp);

    TEST_MUTEX_RELEASE(g_testByTest);
}

TEST_FUNCTION(CodeFirst_Init_succeds)
{
    umock_c_reset_all_calls();

    ///arrange
    CODEFIRST_RESULT result;
    CodeFirst_Deinit();

    ///act
    result = CodeFirst_Init(NULL);

    ///assert
    ASSERT_ARE_EQUAL(CODEFIRST_RESULT, CODEFIRST_OK, result);
}

TEST_FUNCTION(InvokeAction_goToLocation_succeeds)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE someLocation;

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_DOUBLE_TYPE;
    Long.value.edmDouble.value = 4.0;

    COMPLEX_TYPE_FIELD_TYPE fieldsOfSomeLocation[2] = { { "Lat", &Lat }, { "Long", &Long } };

    someLocation.type = EDM_COMPLEX_TYPE_TYPE;
    someLocation.value.edmComplexType.nMembers = 2;
    someLocation.value.edmComplexType.fields = fieldsOfSomeLocation;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &someLocation);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_SUCCESS, result);
    ASSERT_IS_TRUE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(double, 4.0, goToLocation_XMarksTheSpot_long);
    ASSERT_ARE_EQUAL(double, 3.0, goToLocation_XMarksTheSpot_lat);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_alwaysRejected_return_EXECUTE_COMMAND_FAILED)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "alwaysReject", 0, NULL);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_FAILED, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_alwaysAbandon_return_EXECUTE_COMMAND_ERROR)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "alwaysAbandon", 0, NULL);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

/*the below tests are derived from the one above by trying to feed wrong data in the CodeFirst_InvokeAction*/

TEST_FUNCTION(InvokeAction_goToLocation_with_a_struct_with_3_fields_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    static AGENT_DATA_TYPE someLocation;

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_DOUBLE_TYPE;
    Long.value.edmDouble.value = 4.0;

    static AGENT_DATA_TYPE Extra;
    Extra.type = EDM_DOUBLE_TYPE;
    Extra.value.edmDouble.value = 4.0;

    COMPLEX_TYPE_FIELD_TYPE fieldsOfSomeLocation[3] = { { "Lat", &Lat }, { "Long", &Long }, { "Extra", &Extra } };

    someLocation.type = EDM_COMPLEX_TYPE_TYPE;
    someLocation.value.edmComplexType.nMembers = 3;
    someLocation.value.edmComplexType.fields = fieldsOfSomeLocation;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &someLocation);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_struct_with_1_field_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    static AGENT_DATA_TYPE someLocation;

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    COMPLEX_TYPE_FIELD_TYPE fieldsOfSomeLocation[1] = { { "Lat", &Lat } };

    someLocation.type = EDM_COMPLEX_TYPE_TYPE;
    someLocation.value.edmComplexType.nMembers = 1;
    someLocation.value.edmComplexType.fields = fieldsOfSomeLocation;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &someLocation);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_non_struct_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &Lat);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_struct_and_a_double_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE someLocationAndDouble[2];

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_DOUBLE_TYPE;
    Long.value.edmDouble.value = 4.0;

    COMPLEX_TYPE_FIELD_TYPE someLocationAndDouble_1[2] = { { "Lat", &Lat }, { "Long", &Long } };

    someLocationAndDouble[0].type = EDM_COMPLEX_TYPE_TYPE;
    someLocationAndDouble[0].value.edmComplexType.nMembers = 2;
    someLocationAndDouble[0].value.edmComplexType.fields = someLocationAndDouble_1;

    someLocationAndDouble[1].type = EDM_DOUBLE_TYPE;
    someLocationAndDouble[1].value.edmDouble.value = 5.0;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 2, someLocationAndDouble);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_double_and_a_struct_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE someLocationAndDouble[2];

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_DOUBLE_TYPE;
    Long.value.edmDouble.value = 4.0;

    COMPLEX_TYPE_FIELD_TYPE someLocationAndDouble_1[2] = { { "Lat", &Lat }, { "Long", &Long } };

    someLocationAndDouble[1].type = EDM_COMPLEX_TYPE_TYPE;
    someLocationAndDouble[1].value.edmComplexType.nMembers = 2;
    someLocationAndDouble[1].value.edmComplexType.fields = someLocationAndDouble_1;

    someLocationAndDouble[0].type = EDM_DOUBLE_TYPE;
    someLocationAndDouble[0].value.edmDouble.value = 5.0;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
   EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 2, someLocationAndDouble);

    ///assert
   ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_struct_with_first_field_of_wrong_type_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE someLocation;

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_SINGLE_TYPE;
    Lat.value.edmDouble.value = 3.0f;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_DOUBLE_TYPE;
    Long.value.edmDouble.value = 4.0;

    COMPLEX_TYPE_FIELD_TYPE fieldsOfSomeLocation[2] = { { "Lat", &Lat }, { "Long", &Long } };

    someLocation.type = EDM_COMPLEX_TYPE_TYPE;
    someLocation.value.edmComplexType.nMembers = 2;
    someLocation.value.edmComplexType.fields = fieldsOfSomeLocation;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
   EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &someLocation);

    ///assert
   ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_goToLocation_with_a_struct_with_second_field_of_wrong_type_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE someLocation;

    static AGENT_DATA_TYPE Lat;
    Lat.type = EDM_DOUBLE_TYPE;
    Lat.value.edmDouble.value = 3.0;

    static AGENT_DATA_TYPE Long;
    Long.type = EDM_SINGLE_TYPE;
    Long.value.edmDouble.value = 4.0f;

    COMPLEX_TYPE_FIELD_TYPE fieldsOfSomeLocation[2] = { { "Lat", &Lat }, { "Long", &Long } };

    someLocation.type = EDM_COMPLEX_TYPE_TYPE;
    someLocation.value.edmComplexType.nMembers = 2;
    someLocation.value.edmComplexType.fields = fieldsOfSomeLocation;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "goToLocation", 1, &someLocation);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(goToLocation_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_succeeds)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_SUCCESS, result);
    ASSERT_IS_TRUE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(int, 2, moveCarTo_speed);
    ASSERT_ARE_EQUAL(int, 3, moveCarTo_destination_Alt);
    ASSERT_ARE_EQUAL(double, 4.0, moveCarTo_destination_whereIsMyCar_Lat);
    ASSERT_ARE_EQUAL(double, 5.0, moveCarTo_destination_whereIsMyCar_Long);
    ASSERT_IS_TRUE(moveCarTo_reverse);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

/*the below tests are simple to make it fail*/

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_number_of_parameters_fails_1)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 2, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_number_of_parameters_fails_2)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
   EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 4, parameters);

    ///assert
   ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_of_parameter_1_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT64_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_of_parameter_2_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_INT64_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_for_destination_Alt_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT64_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_for_destination_whereIsMyCar_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_INT64_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_for_destination_whereIsMyCar_Lat_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_INT64_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_for_destination_whereIsMyCar_Long_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_INT64_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_wrong_type_for_reverse_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_INT64_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_too_many_fields_in_CarLocation_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 3;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_too_few_fields_in_CarLocation_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 2;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 1;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_too_many_fields_in_CarLocation_whereIsMyCar_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 3;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
    EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
    ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

TEST_FUNCTION(InvokeAction_moveCarTo_with_too_few_fields_in_CarLocation_whereIsMyCar_fails)
{
    ///arrange
    void* device = CodeFirst_CreateDevice(TEST_MODEL_HANDLE, &DummyDataProvider_WithStructs_allReflected, sizeof(TruckType), false);
    umock_c_reset_all_calls();
    AGENT_DATA_TYPE parameters[3];

    parameters[0].type = EDM_INT32_TYPE;
    parameters[0].value.edmInt32.value = 2;

    AGENT_DATA_TYPE CarLocation_Alt;
    CarLocation_Alt.type = EDM_INT32_TYPE;
    CarLocation_Alt.value.edmInt32.value = 3;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCar_Lat.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Lat.value.edmDouble.value = 4.0;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar_Long;
    CarLocation_whereIsMyCar_Long.type = EDM_DOUBLE_TYPE;
    CarLocation_whereIsMyCar_Long.value.edmDouble.value = 5.0;

    COMPLEX_TYPE_FIELD_TYPE CarLocation_whereIsMyCarFields[2];
    CarLocation_whereIsMyCarFields[0].fieldName = "Lat";
    CarLocation_whereIsMyCarFields[0].value = &CarLocation_whereIsMyCar_Lat;
    CarLocation_whereIsMyCarFields[1].fieldName = "Long";
    CarLocation_whereIsMyCarFields[1].value = &CarLocation_whereIsMyCar_Long;

    AGENT_DATA_TYPE CarLocation_whereIsMyCar;
    CarLocation_whereIsMyCar.type = EDM_COMPLEX_TYPE_TYPE;
    CarLocation_whereIsMyCar.value.edmComplexType.nMembers = 1;
    CarLocation_whereIsMyCar.value.edmComplexType.fields = CarLocation_whereIsMyCarFields;

    COMPLEX_TYPE_FIELD_TYPE carLocationFields[2];
    carLocationFields[0].fieldName = "Alt";
    carLocationFields[0].value = &CarLocation_Alt;
    carLocationFields[1].fieldName = "whereIsMyCar";
    carLocationFields[1].value = &CarLocation_whereIsMyCar;

    parameters[1].type = EDM_COMPLEX_TYPE_TYPE;
    parameters[1].value.edmComplexType.nMembers = 2;
    parameters[1].value.edmComplexType.fields = carLocationFields;

    parameters[2].type = EDM_BOOLEAN_TYPE;
    parameters[2].value.edmBoolean.value = EDM_TRUE;

    STRICT_EXPECTED_CALL(Schema_GetModelName(TEST_MODEL_HANDLE)).SetReturn("TruckType");

    ///act
   EXECUTE_COMMAND_RESULT result = CodeFirst_InvokeAction(TEST_DEVICE_HANDLE, g_InvokeActionCallbackArgument, "", "moveCarTo", 3, parameters);

    ///assert
   ASSERT_ARE_EQUAL(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_ERROR, result);
    ASSERT_IS_FALSE(moveCarTo_wasCalled);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    // cleanup
    CodeFirst_DestroyDevice(device);
}

END_TEST_SUITE(CodeFirst_ut_Two_Providers_With_Structs);
