/* SPDX-License-Identifier: BSD-2-Clause */
/***********************************************************************
 * Copyright (c) 2017-2018, Intel Corporation
 *
 * All rights reserved.
 ***********************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h" // IWYU pragma: keep
#endif

#include <stdbool.h> // for bool
#include <stdio.h>   // for NULL
#include <string.h>  // for strcmp

#include "../helper/cmocka_all.h" // for cmocka_unit_test, assert_false
#include "tss2_common.h"          // for TSS2_RC, TSS2_TCTI_RC_BAD_VALUE
#include "util/key-value-parse.h" // for parse_key_value, parse_key_value_s...

/*
 * Ensure that a simple key / value string is parsed into its component parts.
 */
static void
parse_key_value_simple_test(void **state) {
    bool        ret;
    char        test_str[] = "key=value";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_true(ret);
    assert_string_equal(key_value.key, "key");
    assert_string_equal(key_value.value, "value");
}
/*
 * Ensure that a NULL key/value string causes parse_key_value to return false.
 */
static void
parse_key_value_NULL_string_test(void **state) {
    bool        ret;
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(NULL, &key_value);
    assert_false(ret);
}
/*
 * Ensure that a NULL key_value_t parameter causes parse_key_value to return
 * false.
 */
static void
parse_key_value_NULL_key_value_test(void **state) {
    bool ret;
    char test_str[] = "key=value";

    ret = parse_key_value(test_str, NULL);
    assert_false(ret);
}
/*
 * Ensure that an incomplete key/value string with only the "key=" returns
 * false.
 */
static void
parse_key_value_no_value_test(void **state) {
    bool        ret;
    char        test_str[] = "key=";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_false(ret);
}
/*
 * Ensure that a key/value string with only the "=value" part returns false.
 */
static void
parse_key_value_no_key_test(void **state) {
    bool        ret;
    char        test_str[] = "=value";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_false(ret);
}
/*
 * Ensure that a key/value string with the separators in the wrong place
 * returns false.
 */
static void
parse_key_value_two_seps_test(void **state) {
    bool        ret;
    char        test_str[] = "=foo=";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_false(ret);
}
/*
 * Ensure that a key/value string with all separators returns false.
 */
static void
parse_key_value_all_seps_test(void **state) {
    bool        ret;
    char        test_str[] = "====";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_false(ret);
}
/*
 * Ensure that a key/value string that alternates strings and separators
 * will parse the first two and ignore the rest.
 */
static void
parse_key_value_alt_seps_test(void **state) {
    bool        ret;
    char        test_str[] = "key=value=key=value";
    key_value_t key_value = KEY_VALUE_INIT;

    ret = parse_key_value(test_str, &key_value);
    assert_true(ret);
    assert_string_equal(key_value.key, "key");
    assert_string_equal(key_value.value, "value");
}
/*
 * This is a simple data structure used to hold values parsed from a string
 * of key/value pairs.
 */
#define TEST_DATA_INIT                                                                             \
    { .value0 = NULL, .value1 = NULL, }
typedef struct {
    char *value0;
    char *value1;
} test_data_t;
/*
 * This is a callback function used to handle extracted key / value pairs.
 */
TSS2_RC
key_value_callback(const key_value_t *key_value, void *user_data) {
    test_data_t *test_data = (test_data_t *)user_data;

    if (strcmp("key0", key_value->key) == 0) {
        test_data->value0 = key_value->value;
        return TSS2_RC_SUCCESS;
    } else if (strcmp("key1", key_value->key) == 0) {
        test_data->value1 = key_value->value;
        return TSS2_RC_SUCCESS;
    } else {
        return 1;
    }
}
/*
 * This tests the typical case for the parsing of a string of key / value
 * pairs.
 */
static void
parse_key_value_string_good_test(void **state) {
    TSS2_RC     rc;
    char        test_str[] = "key0=value0,key1=value1";
    test_data_t test_data = TEST_DATA_INIT;

    rc = parse_key_value_string(test_str, key_value_callback, &test_data);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
    assert_string_equal(test_data.value0, "value0");
    assert_string_equal(test_data.value1, "value1");
}
/*
 * This test ensures that he parse_key_value_string function handles a failed
 * call to parse a key/value pair properly.
 */
static void
parse_key_value_string_no_value_test(void **state) {
    TSS2_RC     rc;
    char        test_str[] = "key0=,key1=value1";
    test_data_t test_data = TEST_DATA_INIT;

    rc = parse_key_value_string(test_str, key_value_callback, &test_data);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_VALUE);
}
/*
 * This test ensures that the parse_key_value_string function handles a failed
 * call to the user provided callback properly. The return value we get from
 * the parse_key_value_string function is the same value returned by our
 * callback.
 */
static void
parse_key_value_string_unknown_key_test(void **state) {
    TSS2_RC     rc;
    char        test_str[] = "key0=foo=bar,baz=qux";
    test_data_t test_data = TEST_DATA_INIT;

    rc = parse_key_value_string(test_str, key_value_callback, &test_data);
    assert_int_equal(rc, 1);
}
/*
 * The following 3 tests ensures that NULL parameters produce an error.
 */
static void
parse_key_value_string_NULL_kv_string_test(void **state) {
    TSS2_RC     rc;
    test_data_t test_data = TEST_DATA_INIT;

    rc = parse_key_value_string(NULL, key_value_callback, &test_data);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_VALUE);
}
static void
parse_key_value_string_NULL_callback_test(void **state) {
    TSS2_RC     rc;
    char        test_str[] = "key0=foo=bar,baz=qux";
    test_data_t test_data = TEST_DATA_INIT;

    rc = parse_key_value_string(test_str, NULL, &test_data);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_VALUE);
}
static void
parse_key_value_string_NULL_user_data_test(void **state) {
    TSS2_RC rc;
    char    test_str[] = "key0=foo=bar,baz=qux";

    rc = parse_key_value_string(test_str, key_value_callback, NULL);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_VALUE);
}

int
main(int argc, char *argv[]) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(parse_key_value_simple_test),
        cmocka_unit_test(parse_key_value_NULL_string_test),
        cmocka_unit_test(parse_key_value_NULL_key_value_test),
        cmocka_unit_test(parse_key_value_no_value_test),
        cmocka_unit_test(parse_key_value_no_key_test),
        cmocka_unit_test(parse_key_value_two_seps_test),
        cmocka_unit_test(parse_key_value_all_seps_test),
        cmocka_unit_test(parse_key_value_alt_seps_test),
        cmocka_unit_test(parse_key_value_string_good_test),
        cmocka_unit_test(parse_key_value_string_no_value_test),
        cmocka_unit_test(parse_key_value_string_unknown_key_test),
        cmocka_unit_test(parse_key_value_string_NULL_kv_string_test),
        cmocka_unit_test(parse_key_value_string_NULL_callback_test),
        cmocka_unit_test(parse_key_value_string_NULL_user_data_test),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
