/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (c) 2017 - 2018, Intel Corporation
 * All rights reserved.
 */
#include <errno.h>
#include <gio/gunixfdlist.h>
#include <glib.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

#include <setjmp.h>
#include <cmocka.h>

#include <tss2/tss2_tpm2_types.h>

#include "tabrmd.h"
#include "tss2-tcti-tabrmd.h"
#include "tcti-tabrmd-priv.h"
#include "tpm2-header.h"
#include "util.h"

#if defined(__linux__) || defined(__unix__) || defined(__APPLE__)
#define TSS2_TCTI_POLL_HANDLE_ZERO_INIT { .fd = 0, .events = 0, .revents = 0 }
#else
#define TSS2_TCTI_POLL_HANDLE_ZERO_INIT { 0 }
#endif

/*
 * when given an NULL context and a pointer to a size_t, set the size_t
 * parameter to the size of the TSS2_TCTI_TABRMD_CONTEXT structure.
 */
static void
tcti_tabrmd_init_size_test (void **state)
{
    size_t tcti_size;
    UNUSED_PARAM(state);

    Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL);
    assert_int_equal (tcti_size, sizeof (TSS2_TCTI_TABRMD_CONTEXT));
}
/*
 * when given a NULL context and a pointer to size_t, the init function
 * returns TSS2_RC_SUCCESS
 */
static void
tcti_tabrmd_init_success_return_value_test (void **state)
{
    size_t tcti_size;
    TSS2_RC ret;
    UNUSED_PARAM(state);

    ret = Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL);
    assert_int_equal (ret, TSS2_RC_SUCCESS);
}
/*
 * when given NULL pointers for both parameters the init function returns
 * an error indicating that the values are bad (TSS2_TCTI_RC_BAD_VALUE)
 */
static void
tcti_tabrmd_init_allnull_is_bad_value_test (void **state)
{
    TSS2_RC ret;
    UNUSED_PARAM(state);

    ret = Tss2_Tcti_Tabrmd_Init (NULL, NULL, NULL);
    assert_int_equal (ret, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * Ensure that the standard Tss2_Tcti_Info discovery function returns a
 * reference to the config structure.
 */
static void
tcti_tabrmd_info_test (void **state)
{
    const TSS2_TCTI_INFO *tcti_info;
    UNUSED_PARAM(state);

    /*
     * This function isn't meant to be called directly. We do it here for the
     * test coverage
     */
    tcti_info = Tss2_Tcti_Info ();
    assert_non_null (tcti_info);
    assert_non_null (tcti_info->init);
}
/*
 * Ensure that when we pass the string "session" to the
 * tabrmd_bus_type_from_str returns G_BUS_TYPE_SESSION.
 */
static void
tcti_tabrmd_bus_type_from_str_session_test (void **state)
{
    UNUSED_PARAM(state);
    assert_int_equal (tabrmd_bus_type_from_str ("session"),
                      G_BUS_TYPE_SESSION);
}
/*
 * Ensure that when we pass the string "system" to the
 * tabrmd_bus_type_from_str returns G_BUS_TYPE_SYSTEM.
 */
static void
tcti_tabrmd_bus_type_from_str_system_test (void **state)
{
    UNUSED_PARAM(state);
    assert_int_equal (tabrmd_bus_type_from_str ("system"),
                      G_BUS_TYPE_SYSTEM);
}
/*
 * Ensure that when we pass an unexpected string to the
 * tabrmd_bus_type_from_str returns G_BUS_TYPE_NONE.
 */
static void
tcti_tabrmd_bus_type_from_str_bad_test (void **state)
{
    UNUSED_PARAM(state);
    assert_int_equal (tabrmd_bus_type_from_str ("foobar"),
                      G_BUS_TYPE_NONE);
}
/*
 * Ensure that when we pass the key "bus_name" and value "any string"
 * to tabrmd_kv_callback that it returns an RC indicating success while
 * the conf structure 'bus_name' field is set to the value string.
 */
static void
tcti_tabrmd_kv_callback_name_test (void **state)
{
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    key_value_t key_value = { 
        .key = "bus_name",
        .value = "foo.bar",
    };
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tabrmd_kv_callback (&key_value, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_string_equal ("foo.bar", conf.bus_name);
}
/*
 * Ensure that when we pass the key "bus_type" and the value "system"
 * to tabrmd_kv_callback that it returns an RC indicating success while
 * the conf structure 'bus_type' field is set to G_BUS_TYPE_SYSTEM.
 */
static void
tcti_tabrmd_kv_callback_type_good_test (void **state)
{
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    key_value_t key_value = { 
        .key = "bus_type",
        .value = "system",
    };
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tabrmd_kv_callback (&key_value, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_int_equal (conf.bus_type, G_BUS_TYPE_SYSTEM);
}
/*
 * Ensure that when we pass the key "bus_type" and with an invalid value
 * string (not "system" or "session") that it returns the BAD_VALUE RC and
 * sets the 'bus_type' field of the conf structure to
 * G_BUS_TYPE_NONE.
 */
static void
tcti_tabrmd_kv_callback_type_bad_test (void **state)
{
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    key_value_t key_value = { 
        .key = "bus_type",
        .value = "foo",
    };
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tabrmd_kv_callback (&key_value, &conf);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
    assert_int_equal (conf.bus_type, G_BUS_TYPE_NONE);
}
/*
 * Ensure that when we pass an invalid key (not 'bus_type' or 'bus_name')
 * that it returns an RC indicating BAD_VALUE.
 */
static void
tcti_tabrmd_kv_callback_bad_key_test (void **state)
{
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    key_value_t key_value = { 
        .key = "foo",
        .value = "bar",
    };
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tabrmd_kv_callback (&key_value, &conf);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * Ensure that a common config string selecting the session bus with
 * a user supplied name is parsed correctly.
 */
static void
tcti_tabrmd_conf_parse_named_session_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_type=session,bus_name=com.example.Session";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_string_equal (conf.bus_name, "com.example.Session");
    assert_int_equal (conf.bus_type, G_BUS_TYPE_SESSION);
}
/*
 * Ensure that a common config string selecting the system bus with
 * a user supplied name is parsed correctly.
 */
static void
tcti_tabrmd_conf_parse_named_system_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_type=system,bus_name=com.example.System";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_string_equal (conf.bus_name, "com.example.System");
    assert_int_equal (conf.bus_type, G_BUS_TYPE_SYSTEM);
}
/*
 * Ensure that an unknown bus_type string results in the appropriate RC.
 */
static void
tcti_tabrmd_conf_parse_bad_type_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_type=foobar";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * Ensure that a config string that omits the bus_name results in a conf
 * structure with the associated field set to the default.
 */
static void
tcti_tabrmd_conf_parse_no_name_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_type=session";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_string_equal (conf.bus_name, TABRMD_DBUS_NAME_DEFAULT);
    assert_int_equal (conf.bus_type, G_BUS_TYPE_SESSION);
}
/*
 * Ensure that a config string that omits the bus_type results in a conf
 * structure with the associated field set to the default.
 */
static void
tcti_tabrmd_conf_parse_no_type_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_name=com.example.FooBar";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_string_equal (conf.bus_name, "com.example.FooBar");
    assert_int_equal (conf.bus_type, TABRMD_DBUS_TYPE_DEFAULT);
}
/*
 * Ensure that a missing value results in the appropriate RC.
 */
static void
tcti_tabrmd_conf_parse_no_value_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "bus_name=";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * Ensure that a missing key results in the appropriate RC
 */
static void
tcti_tabrmd_conf_parse_no_key_test (void **state)
{
    TSS2_RC rc;
    tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT;
    char conf_str[] = "=some-string";
    UNUSED_PARAM(state);

    rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * This mock function is used to insert error conditions in the TCTI
 * initialization function. The function mocked is generated by
 * gdbus-codegen from the dbus xml. Much of the functionality exposed by
 * that code uses a 'proxy' object used to interact with the remote
 * object (tabrmd in this case). The function mocked here is the one used
 * to connect to the tabrmd.
 *
 * The only tricky part to this function is the handling of the GError
 * object. If the function being mocked failes it returns NULL *and*
 * populates the error parameter passed by the caller with a GError object
 * describing the failure. The first item that *must* be on the mock stack
 * is the TctiTabrmd* being returned. If this is NULL then there MUST be
 * a second object on the mock stack and this MUST be a GError object. The
 * caller will g_clear_error this object for us.
 */
TctiTabrmd *
__wrap_tcti_tabrmd_proxy_new_for_bus_sync (
    GBusType             bus_type,
    GDBusProxyFlags      flags,
    const gchar         *name,
    const gchar         *object_path,
    GCancellable        *cancellable,
    GError             **error)
{
    UNUSED_PARAM (bus_type);
    UNUSED_PARAM (flags);
    UNUSED_PARAM (name);
    UNUSED_PARAM (object_path);
    UNUSED_PARAM (cancellable);

    TctiTabrmd* proxy = mock_type (TctiTabrmd*);
    if (proxy == NULL && error != NULL) {
        *error = mock_type (GError*);
    }
    return proxy;
}

/*
 * Force failure acquiring proxy object.
 */
static void
tcti_tabrmd_proxy_new_fail (void **state)
{
    UNUSED_PARAM (state);
    size_t size = sizeof (TSS2_TCTI_TABRMD_CONTEXT);
    uint8_t buf [sizeof (TSS2_TCTI_TABRMD_CONTEXT)] = { 0 };
    TSS2_RC rc = TSS2_RC_SUCCESS;

    will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync, NULL);
    will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync,
                 g_error_new_literal (1, 1, __func__));
    rc = Tss2_Tcti_Tabrmd_Init ((TSS2_TCTI_CONTEXT*)buf, &size, NULL);
    assert_int_equal (rc, TSS2_TCTI_RC_NO_CONNECTION);
}
/*
 * This is a mock function to control return values from the connection
 * creation logic that invokes the DBus "CreateConnection" function exposed
 * by the tabrmd. This is called by the tcti as part of initializing the
 * TCTI context.
 */
GVariant *
__wrap_g_dbus_proxy_call_with_unix_fd_list_sync (
    GDBusProxy *proxy,
    const gchar *method_name,
    GVariant *parameters,
    GDBusCallFlags flags,
    gint timeout_msec,
    GUnixFDList *fd_list,
    GUnixFDList **out_fd_list,
    GCancellable *cancellable,
    GError **error)
{
    GVariant *variant_array[1] = { 0 }, *variant_tuple;
    gint client_fd;
    guint64 id;
    UNUSED_PARAM(proxy);
    UNUSED_PARAM(method_name);
    UNUSED_PARAM(parameters);
    UNUSED_PARAM(flags);
    UNUSED_PARAM(timeout_msec);
    UNUSED_PARAM(fd_list);
    UNUSED_PARAM(out_fd_list);
    UNUSED_PARAM(cancellable);
    UNUSED_PARAM(error);

    client_fd = mock_type (gint);
    id = mock_type (guint64);
    *out_fd_list = g_unix_fd_list_new_from_array (&client_fd, 1);
    variant_array[0] = g_variant_new_uint64 (id);
    variant_tuple = g_variant_new_tuple (variant_array, 1);

    return variant_tuple;
}
/*
 * This is a mock function to control behavior of the
 * tcti_tabrmd_call_cancel_sync function. This is a function generated from
 * the DBus xml specification. It returns only a TSS2_RC.
 */
gboolean
__wrap_tcti_tabrmd_call_cancel_sync (
    TctiTabrmd *proxy,
    guint64 arg_id,
    guint *out_return_code,
    GCancellable *cancellable,
    GError **error)
{
    UNUSED_PARAM(proxy);
    UNUSED_PARAM(arg_id);
    UNUSED_PARAM(cancellable);
    UNUSED_PARAM(error);

    *out_return_code = mock_type (guint);
    return mock_type (gboolean);
}
/*
 * This is a mock function to control behavior of the
 * tcti_tabrmd_call_set_locality_sync function. This is a function generated from
 * the DBus xml specification. It returns only a TSS2_RC.
 */
gboolean
__wrap_tcti_tabrmd_call_set_locality_sync (
    TctiTabrmd *proxy,
    guint64 arg_id,
    guchar arg_locality,
    guint *out_return_code,
    GCancellable *cancellable,
    GError **error)
{
    UNUSED_PARAM(proxy);
    UNUSED_PARAM(arg_id);
    UNUSED_PARAM(arg_locality);
    UNUSED_PARAM(cancellable);
    UNUSED_PARAM(error);

    *out_return_code = mock_type (guint);
    return mock_type (gboolean);
}
/*
 * Structure to hold data relevant to tabrmd TCTI unit tests.
 */
typedef struct {
    guint64 id;
    gint    client_fd;
    gint    server_fd;
    TSS2_TCTI_CONTEXT  *context;
    TctiTabrmdProxy *proxy;
} data_t;
/*
 * This is the setup function used to create the TCTI context structure for
 * tests that require the initialization be done before the test can be
 * executed. It *must* be paired with a call to the teardown function.
 */
static int
tcti_tabrmd_setup (void **state)
{
    data_t *data;
    TSS2_RC ret = TSS2_RC_SUCCESS;
    size_t tcti_size = 0;
    gint fds [2];
    guint64 id = 666;

    data = calloc (1, sizeof (data_t));
    ret = Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL);
    if (ret != TSS2_RC_SUCCESS) {
        free (data);
        printf ("tss2_tcti_tabrmd_init failed: %d\n", ret);
        return 1;
    }
    data->context = calloc (1, tcti_size);
    if (data->context == NULL) {
        free (data);
        perror ("calloc");
        return 1;
    }
    data->proxy = calloc (1, sizeof (TctiTabrmdProxy));
    will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync, data->proxy);
    g_debug ("preparing g_dbus_proxy_call_with_unix_fd_list_sync mock wrapper");
    assert_int_equal (socketpair (PF_LOCAL, SOCK_STREAM, 0, fds), 0);
    data->client_fd = fds [0];
    data->server_fd = fds [1];
    will_return (__wrap_g_dbus_proxy_call_with_unix_fd_list_sync,
                 data->client_fd);
    data->id = id;
    will_return (__wrap_g_dbus_proxy_call_with_unix_fd_list_sync, id);
    ret = Tss2_Tcti_Tabrmd_Init (data->context, &tcti_size, "bus_type=session");
    assert_int_equal (ret, TSS2_RC_SUCCESS);

    *state = data;
    return 0;
}
/*
 * This is a teardown function to deallocate / cleanup all resources
 * associated with these tests.
 */
static int
tcti_tabrmd_teardown (void **state)
{
    data_t *data = *state;

    tss2_tcti_tabrmd_finalize (data->context);
    close (data->client_fd);
    close (data->server_fd);
    if (data->context)
        free (data->context);
    if (data->proxy)
        free (data->proxy);
    free (data);
    return 0;
}
/*
 * Ensure that after initialization the 'magic' value in the TCTI structure
 * is the one that we expect.
 */
static void
tcti_tabrmd_magic_test (void **state)
{
    data_t *data = *state;

    assert_int_equal (TSS2_TCTI_MAGIC (data->context),
                      TSS2_TCTI_TABRMD_MAGIC);
}
/*
 * Ensure that after initialization the 'version' value in the TCTI structure
 * is the one that we expect.
 */
static void
tcti_tabrmd_version_test (void **state)
{
    data_t *data = *state;

    assert_int_equal (TSS2_TCTI_VERSION (data->context), TSS2_TCTI_TABRMD_VERSION);
}
/*
 * Ensure that after initialization the 'id' value set for the connection is
 * the one taht we expect.
 */
static void
tcti_tabrmd_init_success_test (void **state)
{
    data_t *data = *state;

    assert_int_equal (data->id, TSS2_TCTI_TABRMD_ID (data->context));
}
/*
 * These are a series of tests to ensure that the exposed TCTI functions
 * return the appropriate RC when passed NULL contexts.
 *
 * NOTE: These RCs come from the tss2_tcti.h header and not the tabrmd TCTI
 *       code. Still we test for it here as it's a useful reminder that the
 *       case is covered. Also there's a bug in the TSS2 currently as these
 *       should return BAD_REFERENCE, not BAD_CONTEXT
 */
static void
tcti_tabrmd_transmit_null_context_test (void **state)
{
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tss2_tcti_tabrmd_transmit (NULL, 5, NULL);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE);
}
static void
tcti_tabrmd_cancel_null_context_test (void **state)
{
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tss2_tcti_tabrmd_cancel (NULL);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT);
}
static void
tcti_tabrmd_get_poll_handles_null_context_test (void **state)
{
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tss2_tcti_tabrmd_get_poll_handles (NULL, NULL, NULL);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT);
}
static void
tcti_tabrmd_set_locality_null_context_test (void **state)
{
    TSS2_RC rc;
    UNUSED_PARAM(state);

    rc = tss2_tcti_tabrmd_set_locality (NULL, 5);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT);
}
/*
 * This test makes a single call to the transmit function in the the
 * TSS2_TCTI_CONTEXT function pointer table. It sends a fixed 12 byte TPM
 * command buffer:
 * tag:     TPM2_ST_NO_SESSIONS
 * size:    12 bytes
 * command: 0x0
 * data:    0x01, 0x02
 */
static void
tcti_tabrmd_transmit_success_test (void **state)
{
    data_t *data = *state;
    uint8_t command_in [] = { 0x80, 0x02,
                              0x00, 0x00, 0x00, 0x0c,
                              0x00, 0x00, 0x00, 0x00,
                              0x01, 0x02};
    size_t size = sizeof (command_in);
    uint8_t command_out [sizeof (command_in)] = { 0 };
    TSS2_RC rc;
    ssize_t ret;

    rc = tss2_tcti_tabrmd_transmit (data->context, size, command_in);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    ret = read (data->server_fd, command_out, size);
    assert_int_equal (ret, size);
    ASSERT_NON_NULL(data);
    ASSERT_NON_NULL(data->context);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_RECEIVE);
    assert_memory_equal (command_in, command_out, size);
}
/*
 * This test ensures that the magic value in the context structure is checked
 * before the transmit function executes and that the RC is what we expect.
 */
static void
tcti_tabrmd_transmit_bad_magic_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;
    uint8_t command [12] = { 0 };
    size_t size = 12;

    TSS2_TCTI_MAGIC (data->context) = 1;
    rc = tss2_tcti_tabrmd_transmit (data->context, size, command);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_TRANSMIT);
}
/*
 * This test ensures that the version in the context structure is checked
 * before the transmit function executes and that the RC is what we expect.
 */
static void
tcti_tabrmd_transmit_bad_version_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;
    uint8_t command [12] = { 0 };
    size_t size = 12;

    TSS2_TCTI_VERSION (data->context) = -1;
    rc = tss2_tcti_tabrmd_transmit (data->context, size, command);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_TRANSMIT);
}
/*
 * This test sets the state to RECEIVE and then calls transmit. This should
 * produce a BAD_SEQUENCE error.
 */
static void
tcti_tabrmd_transmit_bad_sequence_receive_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;
    uint8_t command [12] = { 0 };
    size_t size = 12;

    TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE;
    rc = tss2_tcti_tabrmd_transmit (data->context, size, command);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_RECEIVE);
}
/*
 * This test sets the state to FINAL and then calls transmit. This should
 * produce a BAD_SEQUENCE error.
 */
static void
tcti_tabrmd_transmit_bad_sequence_final_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;
    uint8_t command [12] = { 0 };
    size_t size = 12;

    TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_FINAL;
    rc = tss2_tcti_tabrmd_transmit (data->context, size, command);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_FINAL);
}
/*
 * This setup function is a thin wrapper around the main setup. The only
 * additional thing done is to set the state machine to the RECEIVE state
 * to simplify testing the receive function.
 */
static int
tcti_tabrmd_receive_setup (void **state)
{
    data_t *data = NULL;

    tcti_tabrmd_setup (state);
    data = *state;
    TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE;
    return 0;
}
/*
 * This test sets up the call_cancel mock function to return values
 * indicating success. It then ensures that an invocation of the cancel
 * convenience macro invokes the underlying function correctly.
 */
static void
tcti_tabrmd_cancel_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;

    will_return (__wrap_tcti_tabrmd_call_cancel_sync, TSS2_RC_SUCCESS);
    will_return (__wrap_tcti_tabrmd_call_cancel_sync, TRUE);
    rc = tss2_tcti_tabrmd_cancel (data->context);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    ASSERT_NON_NULL(data);
    ASSERT_NON_NULL(data->context);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_RECEIVE);
}
/*
 * This test initializes the TCTI context state to TRANSMIT and then calls
 * the cancel function. This should produce a BAD_SEQUENCE error.
 */
static void
tcti_tabrmd_cancel_bad_sequence_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;

    TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_TRANSMIT;
    rc = tss2_tcti_tabrmd_cancel (data->context);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE);
    assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context),
                      TABRMD_STATE_TRANSMIT);
}
/*
 * This test ensures that when passed a NULL reference for both the handles
 * and num_handles parameters the appropriate RC is returned.
 */
static void
tcti_tabrmd_get_poll_handles_all_null_test (void **state)
{
    data_t *data = *state;
    TSS2_RC rc;

    rc = tss2_tcti_tabrmd_get_poll_handles (data->context, NULL, NULL);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE);
}
/*
 * This test invokes the get_poll_handles function passing in a NULL pointer
 * to the handles variable and a valid reference to the count. It then ensures
 * that the result is some number of handles in num_handles out parameter.
 */
static void
tcti_tabrmd_get_poll_handles_count_test (void **state)
{
    data_t *data = *state;
    size_t num_handles = 0;
    TSS2_RC rc;

    rc = tss2_tcti_tabrmd_get_poll_handles (data->context, NULL, &num_handles);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_true (num_handles > 0);
}
/*
 * This test ensures that when passed a valid handles reference with a
 * num_handles parameter that indicates an insufficient number of elements
 * in the handles array, that we get back the appropriate RC.
 */
static void
tcti_tabrmd_get_poll_handles_bad_handles_count_test (void **state)
{
    data_t *data = *state;
    TSS2_TCTI_POLL_HANDLE handles[5] = {
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT,
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT,
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT
    };
    size_t num_handles = 0;
    TSS2_RC rc;

    rc = tss2_tcti_tabrmd_get_poll_handles (data->context, handles, &num_handles);
    assert_int_equal (rc, TSS2_TCTI_RC_INSUFFICIENT_BUFFER);
}
/*
 * This test invokes the get_poll_handles function passing in valid pointer
 * to an array for the handles and a valid reference to the count. It then
 * ensures that the result is some number of handles in num_handles out
 * parameter as well as valid handles in the array.
 */
static void
tcti_tabrmd_get_poll_handles_handles_test (void **state)
{
    data_t *data = *state;
    TSS2_TCTI_POLL_HANDLE handles[5] = {
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT,
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT,
        TSS2_TCTI_POLL_HANDLE_ZERO_INIT
    };
    size_t num_handles = 5;
    TSS2_RC rc;
    int fd = -1;

    rc = tss2_tcti_tabrmd_get_poll_handles (data->context, handles, &num_handles);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
    assert_int_equal (1, num_handles);
    ASSERT_NON_NULL(data);
    ASSERT_NON_NULL(data->context);
    fd = TSS2_TCTI_TABRMD_FD (data->context);
    assert_int_equal (handles [0].fd, fd);
}
/*
 * This test sets up the call_set_locality mock function to return values
 * indicating success. It then ensures that an invocation of the set_locality
 * convenience macro invokes the underlying function correctly.
 */
static void
tcti_tabrmd_set_locality_test (void **state)
{
    data_t *data = *state;
    uint8_t locality = 0;
    TSS2_RC rc;

    will_return (__wrap_tcti_tabrmd_call_set_locality_sync, TSS2_RC_SUCCESS);
    will_return (__wrap_tcti_tabrmd_call_set_locality_sync, TRUE);
    rc = tss2_tcti_tabrmd_set_locality (data->context, locality);
    assert_int_equal (rc, TSS2_RC_SUCCESS);
}
/*
 * This test invokes the set_locality function with the context in the RECEIVE
 * state. This should produce a BAD_SEQUENCE error.
 */
static void
tcti_tabrmd_set_locality_bad_sequence_test (void **state)
{
    data_t *data = *state;
    uint8_t locality = 0;
    TSS2_RC rc;

    TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE;
    rc = tss2_tcti_tabrmd_set_locality (data->context, locality);
    assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE);
}
int
main (void)
{
    const struct CMUnitTest tests[] = {
        cmocka_unit_test (tcti_tabrmd_init_size_test),
        cmocka_unit_test (tcti_tabrmd_init_success_return_value_test),
        cmocka_unit_test (tcti_tabrmd_init_allnull_is_bad_value_test),
        cmocka_unit_test (tcti_tabrmd_proxy_new_fail),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_init_success_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test (tcti_tabrmd_info_test),
        cmocka_unit_test (tcti_tabrmd_bus_type_from_str_session_test),
        cmocka_unit_test (tcti_tabrmd_bus_type_from_str_system_test),
        cmocka_unit_test (tcti_tabrmd_bus_type_from_str_bad_test),
        cmocka_unit_test (tcti_tabrmd_kv_callback_name_test),
        cmocka_unit_test (tcti_tabrmd_kv_callback_type_good_test),
        cmocka_unit_test (tcti_tabrmd_kv_callback_type_bad_test),
        cmocka_unit_test (tcti_tabrmd_kv_callback_bad_key_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_named_session_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_named_system_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_bad_type_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_no_name_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_no_type_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_no_value_test),
        cmocka_unit_test (tcti_tabrmd_conf_parse_no_key_test),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_magic_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_version_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test (tcti_tabrmd_transmit_null_context_test),
        cmocka_unit_test (tcti_tabrmd_cancel_null_context_test),
        cmocka_unit_test (tcti_tabrmd_get_poll_handles_null_context_test),
        cmocka_unit_test (tcti_tabrmd_set_locality_null_context_test),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_success_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_magic_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_version_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_sequence_receive_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_sequence_final_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_cancel_test,
                                         tcti_tabrmd_receive_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_cancel_bad_sequence_test,
                                         tcti_tabrmd_receive_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_all_null_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_bad_handles_count_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_count_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_handles_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_set_locality_test,
                                         tcti_tabrmd_setup,
                                         tcti_tabrmd_teardown),
        cmocka_unit_test_setup_teardown (tcti_tabrmd_set_locality_bad_sequence_test,
                                         tcti_tabrmd_receive_setup,
                                         tcti_tabrmd_teardown),
    };
    return cmocka_run_group_tests (tests, NULL, NULL);
}
