#include "../src/error.h"
#include "../src/handle.h"

#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "test.h"

static void test_real_fatal_error (void) {
  int child = fork ();
  if (child < 0)
    FATAL ("failed to fork child process");
  else if (child) {
    int wstatus;
    pid_t pid = waitpid (child, &wstatus, 0);
    if (pid != child)
      FATAL ("failed to wait on child process");
    else if (WIFEXITED (wstatus))
      FATAL ("child exited");
    else if (!WIFSIGNALED (wstatus))
      FATAL ("child not signalled");
    else {
      int sig = WTERMSIG (wstatus);
      if (sig != SIGABRT)
        FATAL ("child terminated by signal '%d' (%s) "
               "and not as expected by '%d' (SIGABRT)",
               sig, kissat_signal_name (sig), (int) (SIGABRT));
    }
  } else {
    kissat_reset_signal_handler ();
    kissat_fatal ("real fatal error message triggering 'abort'");
    exit (0);
  }
}

#include <setjmp.h>

static jmp_buf jump_buffer;

static void abort_call_back (void) { longjmp (jump_buffer, 42); }

static void test_fake_fatal_error (void) {
  kissat_call_function_instead_of_abort (abort_call_back);
  int val = setjmp (jump_buffer);
  if (val) {
    kissat_call_function_instead_of_abort (0);
    if (val != 42)
      FATAL ("expected '42' as result from 'setjmp'");
  } else {
    kissat_fatal ("faked fatal error message triggering long jump");
    kissat_call_function_instead_of_abort (0);
    FATAL ("long jump not taken");
  }
}

static void just_return_call_back (void) {}

static void test_just_return_from_fatal_error (void) {
  kissat_call_function_instead_of_abort (just_return_call_back);
  kissat_fatal ("faked just returning fatal error message");
}

static void test_start_of_fatal_error_message (void) {
  kissat_fatal_message_start ();
  printf ("after starting a fatal error message printing this message\n");
  fflush (stdout);
}

void tissat_schedule_error (void) {
  SCHEDULE_FUNCTION (test_real_fatal_error);
  SCHEDULE_FUNCTION (test_fake_fatal_error);
  SCHEDULE_FUNCTION (test_just_return_from_fatal_error);
  SCHEDULE_FUNCTION (test_start_of_fatal_error_message);
}
