// SPDX-License-Identifier: Apache-2.0
/*
 * Copyright IBM Corp. 2021
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "testsupport.h"

char msg_trace[] = "TRACE";
char msg_debug[] = "DEBUG";
char msg_info[] = "INFO";
char msg_warn[] = "WARN";
char msg_error[] = "ERROR";
char msg_fatal[] = "FATAL";

void setUp(void) { /* This is run before EACH TEST */
}

void tearDown(void) {}

void try_log(uint32_t loglvl) {

  // override whatever ZDNN_LOGLEVEL/ZDNN_LOGMODULE are set in env
  log_level = loglvl;
  log_module[0] = '\0';

  char buf_stdout[BUFSIZ] = {0};
  char buf_stderr[BUFSIZ] = {0};

  stdout_to_pipe();
  stderr_to_pipe();

  LOG_TRACE(msg_trace, NO_ARG);
  LOG_DEBUG(msg_debug, NO_ARG);
  LOG_INFO(msg_info, NO_ARG);
  LOG_WARN(msg_warn, NO_ARG);
  LOG_ERROR(msg_error, NO_ARG);
  LOG_FATAL(msg_fatal, NO_ARG);

  restore_stdout(buf_stdout, BUFSIZ);
  restore_stderr(buf_stderr, BUFSIZ);

#define EXPECTS_ONLY_STDOUT(msg)                                               \
  if (strstr(buf_stdout, msg) == NULL) {                                       \
    TEST_FAIL_MESSAGE("can't find " #msg " message in STDOUT");                \
  }                                                                            \
  if (strstr(buf_stderr, msg) != NULL) {                                       \
    TEST_FAIL_MESSAGE("found " #msg " message unexpectedly STDERR");           \
  }

#define EXPECTS_ONLY_STDERR(msg)                                               \
  if (strstr(buf_stderr, msg) == NULL) {                                       \
    TEST_FAIL_MESSAGE("can't find " #msg " message in STDERR");                \
  }                                                                            \
  if (strstr(buf_stdout, msg) != NULL) {                                       \
    TEST_FAIL_MESSAGE("found " #msg " message unexpectedly STDOUT");           \
  }

#define EXPECTS_NEITHER(msg)                                                   \
  if (strstr(buf_stdout, msg) != NULL) {                                       \
    TEST_FAIL_MESSAGE("found " #msg " message unexpectedly STDOUT");           \
  }                                                                            \
  if (strstr(buf_stderr, msg) != NULL) {                                       \
    TEST_FAIL_MESSAGE("found " #msg " message unexpectedly STDERR");           \
  }

  switch (loglvl) {
  case (LOGLEVEL_TRACE):
    EXPECTS_ONLY_STDOUT(msg_trace);
    EXPECTS_ONLY_STDOUT(msg_debug);
    EXPECTS_ONLY_STDOUT(msg_info);
    EXPECTS_ONLY_STDOUT(msg_warn);
    EXPECTS_ONLY_STDERR(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_DEBUG):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_ONLY_STDOUT(msg_debug);
    EXPECTS_ONLY_STDOUT(msg_info);
    EXPECTS_ONLY_STDOUT(msg_warn);
    EXPECTS_ONLY_STDERR(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_INFO):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_NEITHER(msg_debug);
    EXPECTS_ONLY_STDOUT(msg_info);
    EXPECTS_ONLY_STDOUT(msg_warn);
    EXPECTS_ONLY_STDERR(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_WARN):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_NEITHER(msg_debug);
    EXPECTS_NEITHER(msg_info);
    EXPECTS_ONLY_STDOUT(msg_warn);
    EXPECTS_ONLY_STDERR(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_ERROR):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_NEITHER(msg_debug);
    EXPECTS_NEITHER(msg_info);
    EXPECTS_NEITHER(msg_warn);
    EXPECTS_ONLY_STDERR(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_FATAL):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_NEITHER(msg_debug);
    EXPECTS_NEITHER(msg_info);
    EXPECTS_NEITHER(msg_warn);
    EXPECTS_NEITHER(msg_error);
    EXPECTS_ONLY_STDERR(msg_fatal);
    break;
  case (LOGLEVEL_OFF):
    EXPECTS_NEITHER(msg_trace);
    EXPECTS_NEITHER(msg_debug);
    EXPECTS_NEITHER(msg_info);
    EXPECTS_NEITHER(msg_warn);
    EXPECTS_NEITHER(msg_error);
    EXPECTS_NEITHER(msg_fatal);
    break;
  default:
    TEST_FAIL_MESSAGE_FORMATTED("Invalid log level %u", loglvl);
  }
}

void test_off(void) { try_log(LOGLEVEL_OFF); }
void test_fatal(void) { try_log(LOGLEVEL_FATAL); }
void test_err0r(void) { try_log(LOGLEVEL_ERROR); } // "error" confuses jenkins
void test_warn(void) { try_log(LOGLEVEL_WARN); }
void test_info(void) { try_log(LOGLEVEL_INFO); }
void test_debug(void) { try_log(LOGLEVEL_DEBUG); }
void test_trace(void) { try_log(LOGLEVEL_TRACE); }

// log_module with only "testDriver_logger.c" in it
void test_in_logmodule() {
  log_level = LOGLEVEL_INFO;
  strncpy(log_module, __FILE__, LOGMODULE_SIZE);

  char buf_stdout[BUFSIZ] = {0};

  stdout_to_pipe();
  LOG_INFO(msg_info, NO_ARG);
  restore_stdout(buf_stdout, BUFSIZ);

  if (strstr(buf_stdout, msg_info) == NULL) {
    TEST_FAIL_MESSAGE("can't find message message in STDOUT");
  }

  fflush(stdout);
}

// log_module with "testDriver_logger.c" somewhere in the string
void test_in_logmodule2() {
  log_level = LOGLEVEL_INFO;
  strncpy(log_module, "fafafa.c " __FILE__ " lalala.c", LOGMODULE_SIZE);

  char buf_stdout[BUFSIZ] = {0};

  stdout_to_pipe();
  LOG_INFO(msg_info, NO_ARG);
  restore_stdout(buf_stdout, BUFSIZ);

  if (strstr(buf_stdout, msg_info) == NULL) {
    TEST_FAIL_MESSAGE("can't find message message in STDOUT");
  }

  fflush(stdout);
}

// log_module with "testDriver_logger.c" completely not in
void test_not_in_logmodule() {
  log_level = LOGLEVEL_INFO;
  strncpy(log_module, "hahahahaha.c", LOGMODULE_SIZE);

  char buf_stdout[BUFSIZ] = {0};

  stdout_to_pipe();
  LOG_INFO(msg_info, NO_ARG);
  restore_stdout(buf_stdout, BUFSIZ);

  if (strstr(buf_stdout, msg_info) != NULL) {
    TEST_FAIL_MESSAGE("found message unexpectedly STDOUT");
  }

  fflush(stdout);
}

int main(void) {
  UNITY_BEGIN();

  // logger in full form only when ZDNN_CONFIG_DEBUG is on
#ifdef ZDNN_CONFIG_DEBUG

  RUN_TEST(test_trace);
  RUN_TEST(test_debug);
  RUN_TEST(test_info);
  RUN_TEST(test_warn);
  RUN_TEST(test_err0r);
  RUN_TEST(test_fatal);
  RUN_TEST(test_off);

  RUN_TEST(test_in_logmodule);
  RUN_TEST(test_in_logmodule2);
  RUN_TEST(test_not_in_logmodule);

#endif

  return UNITY_END();
}
