/**
 * collectd - src/tests/utils_cmds_test.c
 * Copyright (C) 2016       Sebastian 'tokkee' Harl
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *   Sebastian 'tokkee' Harl <sh at tokkee.org>
 **/

// clang-format off
/*
 * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
 * See Github Issue #3193 for details
 */
#include "utils/common/common.h"
#include "testing.h"
#include "utils/cmds/cmds.h"
// clang-format on

static void error_cb(void *ud, cmd_status_t status, const char *format,
                     va_list ap) {
  if (status == CMD_OK)
    return;

  printf("ERROR[%d]: ", status);
  vprintf(format, ap);
  printf("\n");
  fflush(stdout);
} /* void error_cb */

static cmd_options_t default_host_opts = {
    /* identifier_default_host = */ "dummy-host",
};

static struct {
  char *input;
  cmd_options_t *opts;
  cmd_status_t expected_status;
  cmd_type_t expected_type;
} parse_data[] = {
    /* Valid FLUSH commands. */
    {
        "FLUSH",
        NULL,
        CMD_OK,
        CMD_FLUSH,
    },
    {
        "FLUSH identifier=myhost/magic/MAGIC",
        NULL,
        CMD_OK,
        CMD_FLUSH,
    },
    {
        "FLUSH identifier=magic/MAGIC",
        &default_host_opts,
        CMD_OK,
        CMD_FLUSH,
    },
    {
        "FLUSH timeout=123 plugin=\"A\"",
        NULL,
        CMD_OK,
        CMD_FLUSH,
    },
    /* Invalid FLUSH commands. */
    {
        /* Missing hostname; no default. */
        "FLUSH identifier=magic/MAGIC",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        /* Missing 'identifier' key. */
        "FLUSH myhost/magic/MAGIC",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        /* Invalid timeout. */
        "FLUSH timeout=A",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        /* Invalid identifier. */
        "FLUSH identifier=invalid",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        /* Invalid option. */
        "FLUSH invalid=option",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },

    /* Valid GETVAL commands. */
    {
        "GETVAL myhost/magic/MAGIC",
        NULL,
        CMD_OK,
        CMD_GETVAL,
    },
    {
        "GETVAL magic/MAGIC",
        &default_host_opts,
        CMD_OK,
        CMD_GETVAL,
    },

    /* Invalid GETVAL commands. */
    {
        "GETVAL magic/MAGIC",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "GETVAL",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "GETVAL invalid",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },

    /* Valid LISTVAL commands. */
    {
        "LISTVAL",
        NULL,
        CMD_OK,
        CMD_LISTVAL,
    },

    /* Invalid LISTVAL commands. */
    {
        "LISTVAL invalid",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },

    /* Valid PUTVAL commands. */
    {
        "PUTVAL magic/MAGIC N:42",
        &default_host_opts,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC N:42",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC 1234:42",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC 1234:42 2345:23",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC interval=2 1234:42",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC meta:KEY=\"string_value\" 1234:42",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },
    {
        "PUTVAL myhost/magic/MAGIC meta:KEY='string_value' 1234:42",
        NULL,
        CMD_OK,
        CMD_PUTVAL,
    },

    /* Invalid PUTVAL commands. */
    {
        "PUTVAL magic/MAGIC N:42",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL invalid N:42",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL myhost/magic/MAGIC A:42",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL myhost/magic/MAGIC 1234:A",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL myhost/magic/MAGIC",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL 1234:A",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    {
        "PUTVAL myhost/magic/UNKNOWN 1234:42",
        NULL,
        CMD_PARSE_ERROR,
        CMD_UNKNOWN,
    },
    /*
     * As of collectd 5.x, PUTVAL accepts invalid options.
    {
            "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
            NULL,
            CMD_PARSE_ERROR,
            CMD_UNKNOWN,
    },
    */

    /* Invalid commands. */
    {
        "INVALID",
        NULL,
        CMD_UNKNOWN_COMMAND,
        CMD_UNKNOWN,
    },
    {
        "INVALID interval=2",
        NULL,
        CMD_UNKNOWN_COMMAND,
        CMD_UNKNOWN,
    },
};

DEF_TEST(parse) {
  cmd_error_handler_t err = {error_cb, NULL};
  int test_result = 0;

  for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) {
    char *input = strdup(parse_data[i].input);

    char description[1024];
    cmd_status_t status;
    cmd_t cmd;

    bool result;

    memset(&cmd, 0, sizeof(cmd));

    status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
    ssnprintf(description, sizeof(description),
              "cmd_parse (\"%s\", opts=%p) = "
              "%d (type=%d [%s]); want %d "
              "(type=%d [%s])",
              parse_data[i].input, parse_data[i].opts, status, cmd.type,
              CMD_TO_STRING(cmd.type), parse_data[i].expected_status,
              parse_data[i].expected_type,
              CMD_TO_STRING(parse_data[i].expected_type));
    result = (status == parse_data[i].expected_status) &&
             (cmd.type == parse_data[i].expected_type);
    LOG(result, description);

    /* Run all tests before failing. */
    if (!result)
      test_result = -1;

    cmd_destroy(&cmd);
    free(input);
  }

  return test_result;
}

int main(int argc, char **argv) {
  RUN_TEST(parse);
  END_TEST;
}
