// Copyright(c) 2017-2020, Intel Corporation
//
// Redistribution  and  use  in source  and  binary  forms,  with  or  without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of  source code  must retain the  above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name  of Intel Corporation  nor the names of its contributors
//   may be used to  endorse or promote  products derived  from this  software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED TO,  THE
// IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT  SHALL THE COPYRIGHT OWNER  OR CONTRIBUTORS BE
// LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
// CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT  OF
// SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
// INTERRUPTION)  HOWEVER CAUSED  AND ON ANY THEORY  OF LIABILITY,  WHETHER IN
// CONTRACT,  STRICT LIABILITY,  OR TORT  (INCLUDING NEGLIGENCE  OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <opae/fpga.h>

extern "C" {
fpga_result send_fme_event_request(fpga_handle, fpga_event_handle, int);
fpga_result send_port_event_request(fpga_handle, fpga_event_handle, int);
fpga_result send_uafu_event_request(fpga_handle, fpga_event_handle, uint32_t, int);
fpga_result check_interrupts_supported(fpga_handle, fpga_objtype*);
fpga_result driver_register_event(fpga_handle, fpga_event_type, fpga_event_handle, uint32_t);
fpga_result driver_unregister_event(fpga_handle, fpga_event_type, fpga_event_handle);
int xfpga_plugin_initialize(void);
int xfpga_plugin_finalize(void);
}

#include "intel-fpga.h"
#include <stdlib.h>
#include <unistd.h>
#include <chrono>
#include <thread>
#include <string>
#include <cstring>
#include "types_int.h"
#include "gtest/gtest.h"
#include "mock/test_system.h"
#include "mock/fpgad_control.h"

#include "error_int.h"
#include "xfpga.h"
#include <opae/enum.h>
#include <opae/properties.h>
#include <opae/access.h>
#include <linux/ioctl.h>
#include <poll.h>
#include <dlfcn.h>

#undef FPGA_MSG
#define FPGA_MSG(fmt, ...) \
	printf("MOCK " fmt "\n", ## __VA_ARGS__)

#undef FPGA_ERR
#define FPGA_ERR(fmt, ...) \
	printf("MOCK ERROR " fmt "\n", ## __VA_ARGS__)

using namespace opae::testing;

static std::string sysfs_fme = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.0";
static std::string dev_fme = "/dev/intel-fpga-fme.0";
static std::string sysfs_port = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-port.0";
static std::string dev_port = "/dev/intel-fpga-port.0";
static bool gEnableIRQ = false;

static void get_path(const std::string object_type, fpga_handle handle){
  auto h = (struct _fpga_handle*)handle; 
  auto tok = (struct _fpga_token*)h->token; 
  if (object_type.compare("fme") == 0)
  {
     sysfs_fme = tok->sysfspath;
     dev_fme = tok->devpath;
  }   
  else if (object_type.compare("port") == 0)
  {
     sysfs_port = tok->sysfspath;
     dev_port = tok->devpath;
  }
}

int port_info(mock_object * m, int request, va_list argp){
  int retval = -1;
  errno = EINVAL;
  UNUSED_PARAM(m);
  UNUSED_PARAM(request);
  struct fpga_port_info *pinfo = va_arg(argp, struct fpga_port_info *);
  if (!pinfo) {
  	FPGA_MSG("pinfo is NULL");
  	goto out_EINVAL;
  }
  if (pinfo->argsz != sizeof(*pinfo)) {
  	FPGA_MSG("wrong structure size");
  	goto out_EINVAL;
  }
  pinfo->flags = 0;
  pinfo->num_regions = 1;
  pinfo->num_umsgs = 8;
  if (gEnableIRQ) {
  	pinfo->capability = FPGA_PORT_CAP_ERR_IRQ | FPGA_PORT_CAP_UAFU_IRQ;
  	pinfo->num_uafu_irqs = 1;
  } else {
  	pinfo->capability = 0;
  	pinfo->num_uafu_irqs = 0;
  }
  retval = 0;
  errno = 0;
out:
  va_end(argp);
  return retval;

out_EINVAL:
  retval = -1;
  errno = EINVAL;
  goto out;
}

int fme_info(mock_object * m, int request, va_list argp){
  int retval = -1;
  errno = EINVAL;
  UNUSED_PARAM(m);
  UNUSED_PARAM(request);
  struct fpga_fme_info *fme_info = va_arg(argp, struct fpga_fme_info *);
  if (!fme_info) {
  	FPGA_MSG("fme_info is NULL");
  	goto out_EINVAL;
  }
  if (fme_info->argsz != sizeof(*fme_info)) {
  	FPGA_MSG("wrong structure size");
  	goto out_EINVAL;
  }
  if (fme_info->flags != 0) {
  	FPGA_MSG("unexpected flags %u", fme_info->flags);
  	goto out_EINVAL;
  }
  if (fme_info->capability != 0) {
  	FPGA_MSG("unexpected capability %u", fme_info->capability);
  	goto out_EINVAL;
  }
  fme_info->capability = gEnableIRQ ? FPGA_FME_CAP_ERR_IRQ : 0;
  retval = 0;
  errno = 0;
out:
  va_end(argp);
  return retval;

out_EINVAL:
  retval = -1;
  errno = EINVAL;
  goto out;
}

int set_port_irq(mock_object * m, int request, va_list argp){
  int retval = -1;
  errno = EINVAL;
  UNUSED_PARAM(m);
  UNUSED_PARAM(request);
  struct fpga_port_err_irq_set *port_irq = va_arg(argp, struct fpga_port_err_irq_set *);
  if (!port_irq) {
  	FPGA_MSG("port_irq is NULL");
  	goto out_EINVAL;
  }
  if (port_irq->argsz != sizeof(*port_irq)) {
  	FPGA_MSG("wrong structure size");
  	goto out_EINVAL;
  }
  if (port_irq->flags != 0) {
  	FPGA_MSG("unexpected flags %u", port_irq->flags);
  	goto out_EINVAL;
  }
  if (gEnableIRQ && port_irq->evtfd >= 0) {
  	uint64_t data = 1;
  	// Write to the eventfd to signal one IRQ event.
  	if (write(port_irq->evtfd, &data, sizeof(data)) != sizeof(data)) {
  		FPGA_ERR("IRQ write < 8 bytes");
  	}
  }
  retval = 0;
  errno = 0; 
out:
  va_end(argp);
  return retval;

out_EINVAL:
  retval = -1;
  errno = EINVAL;
  goto out;
}

int set_fme_irq(mock_object * m, int request, va_list argp){
  int retval = -1;
  errno = EINVAL;
  UNUSED_PARAM(m);
  UNUSED_PARAM(request);
  struct fpga_fme_err_irq_set *fme_irq = va_arg(argp, struct fpga_fme_err_irq_set *);
  if (!fme_irq) {
  	FPGA_MSG("fme_irq is NULL");
  	goto out_EINVAL;
  }
  if (fme_irq->argsz != sizeof(*fme_irq)) {
  	FPGA_MSG("wrong structure size");
  	goto out_EINVAL;
  }
  if (fme_irq->flags != 0) {
  	FPGA_MSG("unexpected flags %u", fme_irq->flags);
  	goto out_EINVAL;
  }
  if (gEnableIRQ && fme_irq->evtfd >= 0) {
  	uint64_t data = 1;
  	// Write to the eventfd to signal one IRQ event.
  	if (write(fme_irq->evtfd, &data, sizeof(data)) != sizeof(data)) {
  		FPGA_ERR("IRQ write < 8 bytes");
  	}
  }
  retval = 0;
  errno = 0;
out:
  va_end(argp);
  return retval;

out_EINVAL:
  retval = -1;
  errno = EINVAL;
  goto out;
}

int set_uport_irq(mock_object * m, int request, va_list argp){
  int retval = -1;
  errno = EINVAL;
  UNUSED_PARAM(m);
  UNUSED_PARAM(request);
  struct fpga_port_uafu_irq_set *uafu_irq =
  	va_arg(argp, struct fpga_port_uafu_irq_set *);
  if (!uafu_irq) {
  	FPGA_MSG("uafu_irq is NULL");
  	goto out_EINVAL;
  }
  if (uafu_irq->argsz < sizeof(*uafu_irq)) {
  	FPGA_MSG("wrong structure size");
  	goto out_EINVAL;
  }
  if (uafu_irq->flags != 0) {
  	FPGA_MSG("unexpected flags %u", uafu_irq->flags);
  	goto out_EINVAL;
  }
  if (gEnableIRQ) {
  	uint32_t i;
  	uint64_t data = 1;
  	// Write to each eventfd to signal one IRQ event.
  	for (i = 0 ; i < uafu_irq->count ; ++i) {
  		if (uafu_irq->evtfd[i] >= 0)
  			if (write(uafu_irq->evtfd[i], &data, sizeof(data)) !=
  					sizeof(data)) {
  				FPGA_ERR("IRQ write < 8 bytes");
  			}
  	}
  }
  retval = 0;
  errno = 0;
out:
  va_end(argp);
  return retval;

out_EINVAL:
  retval = -1;
  errno = EINVAL;
  goto out;
}

class events_p : public ::testing::TestWithParam<std::string>,
                 public fpgad_control {
 protected:
  events_p()
      : tokens_dev_{{nullptr, nullptr}},
        tokens_accel_{{nullptr, nullptr}},
        handle_dev_(nullptr),
        handle_accel_(nullptr) {}

  virtual void SetUp() override {
    std::string platform_key = GetParam();
    ASSERT_TRUE(test_platform::exists(platform_key));
    platform_ = test_platform::get(platform_key);
    system_ = test_system::instance();
    system_->initialize();
    system_->prepare_syfs(platform_);

    ASSERT_EQ(FPGA_OK, xfpga_plugin_initialize());

    ASSERT_EQ(xfpga_fpgaGetProperties(nullptr, &filter_dev_), FPGA_OK);
    ASSERT_EQ(fpgaPropertiesSetDeviceID(filter_dev_, 
                                        platform_.devices[0].device_id), FPGA_OK);
    ASSERT_EQ(fpgaPropertiesSetObjectType(filter_dev_, FPGA_DEVICE), FPGA_OK);
    ASSERT_EQ(xfpga_fpgaEnumerate(&filter_dev_, 1, tokens_dev_.data(), tokens_dev_.size(),
                            &num_matches_dev_), FPGA_OK);
    ASSERT_GT(num_matches_dev_, 0);

    ASSERT_EQ(xfpga_fpgaGetProperties(nullptr, &filter_accel_), FPGA_OK);
    ASSERT_EQ(fpgaPropertiesSetDeviceID(filter_accel_, 
                                        platform_.devices[0].device_id), FPGA_OK);
    ASSERT_EQ(fpgaPropertiesSetObjectType(filter_accel_, FPGA_ACCELERATOR), FPGA_OK);
    ASSERT_EQ(xfpga_fpgaEnumerate(&filter_accel_, 1, tokens_accel_.data(),
                            tokens_accel_.size(), &num_matches_), FPGA_OK);
    ASSERT_GT(num_matches_, 0);
    ASSERT_EQ(xfpga_fpgaOpen(tokens_dev_[0], &handle_dev_, 0), FPGA_OK);
    ASSERT_EQ(xfpga_fpgaOpen(tokens_accel_[0], &handle_accel_, 0), FPGA_OK);

    get_path("fme", handle_dev_);
    get_path("port", handle_accel_);

    ASSERT_EQ(xfpga_fpgaCreateEventHandle(&eh_), FPGA_OK);

    fpgad_start();

    uint32_t i;
    for (i = 0 ; i < num_matches_dev_ ; ++i) {
      fpgad_watch(tokens_dev_[i]);
    }
    for (i = 0 ; i < num_matches_ ; ++i) {
      fpgad_watch(tokens_accel_[i]);
    }
  }

  virtual void TearDown() override {
    fpgad_stop();

    EXPECT_EQ(xfpga_fpgaDestroyEventHandle(&eh_), FPGA_OK);

    EXPECT_EQ(fpgaDestroyProperties(&filter_dev_), FPGA_OK);
    EXPECT_EQ(fpgaDestroyProperties(&filter_accel_), FPGA_OK);

    if (handle_dev_) { 
        EXPECT_EQ(xfpga_fpgaClose(handle_dev_), FPGA_OK); 
        handle_dev_ = nullptr;
    }

    if (handle_accel_) { 
        EXPECT_EQ(xfpga_fpgaClose(handle_accel_), FPGA_OK); 
        handle_accel_ = nullptr;
    }
 
    for (auto &t : tokens_dev_) {
      if (t) {
        EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyToken(&t));
        t = nullptr;
      }
    }

    for (auto &t : tokens_accel_) {
      if (t) {
        EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyToken(&t));
        t = nullptr;
      }
    }
    xfpga_plugin_finalize();
    system_->finalize();
  }

  fpga_properties filter_dev_;
  fpga_properties filter_accel_;
  std::array<fpga_token, 2> tokens_dev_;
  std::array<fpga_token, 2> tokens_accel_;
  fpga_handle handle_dev_;
  fpga_handle handle_accel_;
  uint32_t num_matches_dev_;
  uint32_t num_matches_;
  test_platform platform_;
  test_system *system_;
  fpga_event_handle eh_;
};

/*
 * @test       event_01
 *
 * @brief      When the fpga_event_handle pointer to
 *             fpgaCreateEventHandle() is NULL, the function returns
 *             FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_01) {
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaCreateEventHandle(NULL));
}

/**
 * @test       event_02
 *
 * @brief      When the fpga_event_handle pointer to
 *             fpgaDestroyEventHandle() is NULL, the function returns
 *             FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_02) {
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaDestroyEventHandle(NULL));
}

/**
 * @test       event_03
 *
 * @brief      Tests fpgaRegisterEvent()'s ability to detect invalid
 *             arguments. When the handle is NULL or otherwise invalid,
 *             FPGA_INVALID_PARAM. When the handle has an invalid token,
 *             FPGA_INVALID_PARAM. When the handle's token describes a
 *             device for which the given event does't apply,
 *             FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_03) {
  fpga_event_type e = FPGA_EVENT_ERROR;
  fpga_event_handle eh;

  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&eh));

  // NULL handle.
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(NULL, e, eh, 0));

  // handle with bad magic.
  struct _fpga_handle _h;
  struct _fpga_token _t;

  // token setup
  strncpy(_t.sysfspath, sysfs_port.c_str(), sysfs_port.size() + 1);
  strncpy(_t.devpath, dev_port.c_str(), dev_port.size() + 1);
  _t.magic = FPGA_TOKEN_MAGIC;
  _t.errors = nullptr;
  std::string errpath = sysfs_port + "/errors";
  build_error_list(errpath.c_str(), &_t.errors);

  memset(&_h, 0, sizeof(_h));
  _h.token = &_t;
  _h.magic = FPGA_INVALID_MAGIC;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(&_h, e, eh, 0));

  // handle with bad token.
  _t.magic = FPGA_INVALID_MAGIC;
  _h.magic = FPGA_HANDLE_MAGIC;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(&_h, e, eh, 0));

  // token/event mismatch.
  strncpy(_t.sysfspath, sysfs_fme.c_str(), sysfs_fme.size() + 1);
  strncpy(_t.devpath, dev_fme.c_str(), dev_fme.size() + 1);
  _t.magic = FPGA_TOKEN_MAGIC;
  _t.errors = nullptr;
  errpath = sysfs_fme + "/errors";
  build_error_list(errpath.c_str(), &_t.errors);

  EXPECT_EQ(FPGA_INVALID_PARAM,
            xfpga_fpgaRegisterEvent(&_h, FPGA_EVENT_INTERRUPT, eh, 0));

  EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyEventHandle(&eh));
}

/**
 * @test       event_04
 *
 * @brief      Tests fpgaUnregisterEvent()'s ability to detect invalid
 *             arguments. When the handle is NULL or otherwise invalid,
 *             FPGA_INVALID_PARAM. When the handle has an invalid token,
 *             FPGA_INVALID_PARAM. When the handle's token describes a
 *             device for which the given event does't apply,
 *             FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_04) {
  fpga_event_type e = FPGA_EVENT_ERROR;
  fpga_event_handle eh;

  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&eh));

  // NULL handle.
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(NULL, e, eh));

  // handle with bad magic.
  struct _fpga_handle _h;
  struct _fpga_token _t;

  // token setup
  strncpy(_t.sysfspath, sysfs_port.c_str(), sysfs_port.size() + 1);
  strncpy(_t.devpath, dev_port.c_str(), dev_port.size() + 1);
  _t.magic = FPGA_TOKEN_MAGIC;
  _t.errors = nullptr;
  std::string errpath = sysfs_port + "/errors";
  build_error_list(errpath.c_str(), &_t.errors);

  memset(&_h, 0, sizeof(_h));
  _h.token = &_t;
  _h.magic = FPGA_INVALID_MAGIC;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(&_h, e, eh));

  // handle with bad token.
  _t.magic = FPGA_INVALID_MAGIC;
  _h.magic = FPGA_HANDLE_MAGIC;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(&_h, e, eh));

  // token/event mismatch.
  strncpy(_t.sysfspath, sysfs_fme.c_str(), sysfs_fme.size() + 1);
  strncpy(_t.devpath, dev_fme.c_str(), dev_fme.size() + 1);
  _t.magic = FPGA_TOKEN_MAGIC;
  _t.errors = nullptr;
  errpath = sysfs_fme + "/errors";
  build_error_list(errpath.c_str(), &_t.errors);

  EXPECT_EQ(FPGA_INVALID_PARAM,
            xfpga_fpgaUnregisterEvent(&_h, FPGA_EVENT_INTERRUPT, eh));

  EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyEventHandle(&eh));
}

/**
 * @test       event_drv_08
 *
 * @brief      When passed an event handle with an invalid magic
 *             fpgaDestroyEventHandle() returns FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_drv_08) {
  fpga_event_handle bad_handle;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));
  struct _fpga_event_handle *h = (struct _fpga_event_handle *) bad_handle;

  // Invalid Event Handle magic
  h->magic=0x0;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaDestroyEventHandle(&bad_handle));

  // reset event handle magic and destroy
  h->magic=FPGA_EVENT_HANDLE_MAGIC;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyEventHandle(&bad_handle));
}

/**
 * @test       event_drv_09
 *
 * @brief      When passed an event handle with an invalid fd
 *             fpgaDestroyEventHandle() returns FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_drv_09) {
  fpga_event_handle bad_handle;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));
  struct _fpga_event_handle *h = (struct _fpga_event_handle *) bad_handle;

  // Invalid fd
  auto fddev = h->fd;
  h->fd = -1;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaDestroyEventHandle(&bad_handle));

  // Reset fd to destroy event handle
  h->fd = fddev;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyEventHandle(&bad_handle));
}

/**
 * @test       event_drv_10
 *
 * @brief      When passed an event handle with an invalid magic
 *             fpgaGetOSObjectFromEventHandle() returns FPGA_INVALID_PARAM.
 *
 */
TEST(events, event_drv_10) {
  fpga_event_handle bad_handle;
  int fd;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));
  struct _fpga_event_handle *h = (struct _fpga_event_handle *) bad_handle;

  // Invalid event handle magic
  h->magic=0x0;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaGetOSObjectFromEventHandle(bad_handle, &fd));

  // Reset event handle magic
  h->magic=FPGA_EVENT_HANDLE_MAGIC;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaGetOSObjectFromEventHandle(bad_handle, &fd));
  EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyEventHandle(&bad_handle));
}

/**
 * @test       register_event
 *
 * @brief      When a valid fpga_event_handle and event types
 *             are passed to fpgaRegisterEvent and fpgaUnregisterEvent.
 *             both API calls return FPGA_OK.
 */
TEST_P(events_p, register_event) {
  fpga_result res;
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
 
  ASSERT_EQ(res = xfpga_fpgaRegisterEvent(handle_dev_, FPGA_EVENT_ERROR, eh_, 0), FPGA_OK)
      << "\tEVENT TYPE: ERROR, RESULT: " << fpgaErrStr(res);
  EXPECT_EQ(res = xfpga_fpgaUnregisterEvent(handle_dev_, FPGA_EVENT_ERROR, eh_), FPGA_OK)
      << "\tRESULT: " << fpgaErrStr(res);
}

/**
 * @test       event_drv_11
 *
 * @brief      When passed an event handle with an invalid magic
 *             xfpga_fpgaUnregisterEvent() returns FPGA_INVALID_PARAM.
 *
 */
TEST_P(events_p, event_drv_11) {
  fpga_event_handle bad_handle = nullptr;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));
  struct _fpga_event_handle *h = (struct _fpga_event_handle *) bad_handle;
  // Invalid event handle magic
  auto valid_magic = h->magic;
  h->magic = 0x0;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT, bad_handle));

  h->magic = valid_magic;
  EXPECT_EQ(xfpga_fpgaDestroyEventHandle(&bad_handle), FPGA_OK);
}

/**
 * @test       event_drv_12
 *
 * @brief      When passed an event handle with an invalid magic
 *             xfpga_fpgaRegisterEvent() returns FPGA_INVALID_PARAM.
 *
 */
TEST_P(events_p, event_drv_12) {
  fpga_event_handle bad_handle = nullptr;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));
  struct _fpga_event_handle *h = (struct _fpga_event_handle *) bad_handle;

  auto valid_magic = h->magic;
  // Invalid event handle magic
  h->magic = 0x0;
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT, bad_handle, 0));
  h->magic = valid_magic;
  EXPECT_EQ(xfpga_fpgaDestroyEventHandle(&bad_handle), FPGA_OK);
}

/**
 * @test       create_destory_invalid
 * @brief      Given a malloc failure, fpgaCreateEventHandle returns 
 *             FPGA_NO_MEMORY.
 */
TEST_P(events_p, create_destroy_invalid) {
  // fail malloc to check edge case
  EXPECT_EQ(xfpga_fpgaDestroyEventHandle(&eh_), FPGA_OK);
  test_system::instance()->invalidate_malloc();

  auto res = xfpga_fpgaCreateEventHandle(&eh_);
  EXPECT_EQ(FPGA_NO_MEMORY,res);
  ASSERT_EQ(xfpga_fpgaCreateEventHandle(&eh_), FPGA_OK);
}

/**
 * @test       irq_event_04
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called with<br>
 *             an invalid handle<br>
 *             then the call fails with FPGA_INVALID_PARAM.<br>
 *             Repeat for fpgaUnregisterEvent.<br>
 *
 */
TEST_P(events_p, irq_event_04) {
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(NULL, FPGA_EVENT_INTERRUPT,
                                       eh_, 0));
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(NULL, FPGA_EVENT_INTERRUPT,
                                       eh_));
}

/**
 * @test       irq_event_05
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called with<br>
 *             an invalid event handle<br>
 *             then the call fails with FPGA_INVALID_PARAM.<br>
 *             Repeat for fpgaUnregisterEvent.<br>
 *             Repeat for fpgaDestroyEventHandle.<br>
 *
 */
TEST_P(events_p, irq_event_05) {
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT,
                                       NULL, 0));
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT,
                                         NULL));
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaDestroyEventHandle(NULL));
}

/**
 * @test       irq_event_06
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called for<br>
 *             an FPGA_DEVICE and FPGA_EVENT_INTERRUPT<br>
 *             then the call fails with FPGA_INVALID_PARAM.<br>
 *             Repeat for fpgaUnregisterEvent.<br>
 */
TEST_P(events_p, irq_event_06) {
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaRegisterEvent(handle_dev_, FPGA_EVENT_INTERRUPT,
                                       eh_, 0));
  EXPECT_EQ(FPGA_INVALID_PARAM, xfpga_fpgaUnregisterEvent(handle_dev_, FPGA_EVENT_INTERRUPT,
                                       eh_));
}

INSTANTIATE_TEST_CASE_P(events, events_p,
                        ::testing::ValuesIn(test_platform::platforms({ "skx-p","dcp-rc" })));


class events_dcp_p : public events_p {};

/**
 * @test       send_port_event_request
 * @brief      When passed a valid event handle, handle and flag.
 *             It returns FPGA_OK for dcp only.
 */
TEST_P(events_dcp_p, invalid_port_event_request){
  int port_op = FPGA_IRQ_ASSIGN;
  auto res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_OK, res) << "\t result is " << res;

  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_OK, res);
}

/**
 * @test       send_fme_event_request
 * @brief      When passed a valid event handle, handle and flag.
 *             It returns FPGA_OK for dcp only.
 */
TEST_P(events_dcp_p, invalid_fme_event_request){
  int fme_op = FPGA_IRQ_ASSIGN;
  auto res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_OK, res) << "\t result is " << res;

  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_OK, res);
}

INSTANTIATE_TEST_CASE_P(events, events_dcp_p,
                        ::testing::ValuesIn(test_platform::hw_platforms({"dcp-rc" })));


class events_mcp_p : public events_p {};

/**
 * @test       send_port_event_request
 * @brief      When passed a valid event handle, handle and flag.
 *             It returns FPGA_NOT_SUPPORTED if interrupt is not
 *             supported or ioctl fails.
 */
TEST_P(events_mcp_p, invalid_port_event_request){
  int port_op = FPGA_IRQ_ASSIGN;
  auto res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED, res) << "\t result is " << res;

  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED, res);
}

/**
 * @test       send_fme_event_request
 * @brief      When passed a valid event handle, handle and flag.
 *             It returns FPGA_NOT_SUPPORTED if interrupt is not
 *             supported or ioctl fails.
 */
TEST_P(events_mcp_p, invalid_fme_event_request){
  int fme_op = FPGA_IRQ_ASSIGN;
  auto res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res) << "\t result is " << res;

  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);
}

INSTANTIATE_TEST_CASE_P(events, events_mcp_p,
                        ::testing::ValuesIn(test_platform::platforms({"skx-p" })));


class events_mock_p : public events_p {
};

/**
 * @test       register_event_02
 *
 * @brief      When a valid fpga_event_handle and event types
 *             are passed to fpgaRegisterEvent and fpgaUnregisterEvent.
 *             both API calls return FPGA_OK.
 */
TEST_P(events_mock_p, register_event_02) {
  fpga_result res;
  ASSERT_EQ(res = xfpga_fpgaRegisterEvent(handle_dev_, FPGA_EVENT_POWER_THERMAL, eh_, 0),
            FPGA_OK)
      << "\tEVENT TYPE: ERROR, RESULT: " << fpgaErrStr(res);
  EXPECT_EQ(res = xfpga_fpgaUnregisterEvent(handle_dev_, FPGA_EVENT_POWER_THERMAL, eh_),
            FPGA_OK)
      << "\tRESULT: " << fpgaErrStr(res);
}

/**
 * @test       send_fme_event_request
 *
 * @brief      When passed a valid event handle, handle
 *             with FPGA_IRQ_ASSIGN flag.
 *             The function return FPGA_OK.
 *
 */
TEST_P(events_mock_p, valid_fme_event_request){
  int fme_op = FPGA_IRQ_ASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  auto res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_port_event_request
 * @brief      When passed a valid event handle and handle
 *             with FPGA_IRQ_ASSIGN flag. The function 
 *             returns FPGA_OK.
 */
TEST_P(events_mock_p, valid_port_event_request){
  int port_op = FPGA_IRQ_ASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_port_event_request
 * @brief      When passed a valid event handle and handle
 *             with FPGA_IRQ_DEASSIGN flag. The function 
 *             returns FPGA_OK.
 */
TEST_P(events_mock_p, valid_port_event_request_01){
  int port_op = FPGA_IRQ_DEASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = send_port_event_request(handle_accel_,eh_,port_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_fme_event_request
 *
 * @brief      When passed a valid event handle and handle
 *             with FPGA_IRQ_DEASSIGN flag. The function 
 *             returns FPGA_OK.
 *
 */
TEST_P(events_mock_p, valid_fme_event_request_01){
  int fme_op = FPGA_IRQ_DEASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  auto res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_uafu_event_request
 * @brief      When passed a valid event handle and handle
 *             with FPGA_IRQ_ASSIGN flag. The function 
 *             returns FPGA_OK.
 */
TEST_P(events_mock_p, valid_uafu_event_request){
  int port_op = FPGA_IRQ_ASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_uafu_event_request
 * @brief      When passed a valid event handle and handle
 *             with FPGA_IRQ_DEASSIGN flag. The function
 *             returns FPGA_OK.
 */
TEST_P(events_mock_p, valid_uafu_event_request_01){
  int port_op = FPGA_IRQ_DEASSIGN;

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_OK,res);
}

/**
 * @test       send_uafu_event_request
 * @brief      When passed a valid event handle, handle and port params
 *             but an invalid interrupt num. It returns FPGA_INVALID_PARAM.
 */
TEST_P(events_mock_p, invalid_uafu_event_request_03){
  int port_op = FPGA_IRQ_ASSIGN;
  gEnableIRQ = true;

  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = send_uafu_event_request(handle_dev_,eh_,2,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       afu_driver_register_event
 * @brief      Given invalid flags to driver_register_event, it
 *             returns FPGA_INVALID_PARAM. FPGA_EVENT_POWER_THERMAL 
 *             is not supported. 
 */
TEST_P(events_mock_p, afu_driver_register_event){
  int port_op = FPGA_IRQ_ASSIGN;

  // Valid params
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = driver_register_event(handle_accel_, FPGA_EVENT_ERROR, eh_, 0);
  EXPECT_EQ(FPGA_OK,res);

  // Invalid num_uafu_irqs
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  res = driver_register_event(handle_accel_, FPGA_EVENT_INTERRUPT, eh_, port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  // Not supported event type
  res = driver_register_event(handle_accel_, FPGA_EVENT_POWER_THERMAL, eh_, 0);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);
}

/**
 * @test       fme_driver_register_event
 * @brief      Given invalid flags to driver_register_event, it
 *             returns FPGA_INVALID_PARAM. 
 */
TEST_P(events_mock_p, fme_driver_register_event){
  // Invalid ioctl
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  auto res = driver_register_event(handle_dev_, FPGA_EVENT_ERROR, eh_, 0);
  EXPECT_EQ(FPGA_OK,res);

  res = driver_register_event(handle_dev_, FPGA_EVENT_INTERRUPT, eh_, 0);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       fme_driver_unregister_event
 * @brief      Given invalid event type to fme, driver_unregister_event
 *             returns FPGA_INVALID_PARAM.
 */
TEST_P(events_mock_p, fme_driver_unregister_event){
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  auto res = driver_unregister_event(handle_dev_, FPGA_EVENT_ERROR, eh_);
  EXPECT_EQ(FPGA_OK,res);

  // Not supported event_type
  res = driver_unregister_event(handle_dev_, FPGA_EVENT_INTERRUPT, eh_);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       event_drv_13
 *
 * @brief      When register a valid event handle, FPGA_EVENT_INTERRUPT
 *             xfpga_fpgaRegisterEvent() returns FPGA_OK.
 *
 */
TEST_P(events_mock_p, event_drv_13) {
  fpga_event_handle bad_handle;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));

  // Reset event handle magic
  EXPECT_EQ(FPGA_OK, xfpga_fpgaRegisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT, bad_handle, 0));

  // Destroy event handle
  auto res = xfpga_fpgaDestroyEventHandle(&bad_handle);
  EXPECT_EQ(FPGA_OK, res);
}

/**
 * @test       event_drv_14
 *
 * @brief      When passed an event handle with an invalid magic
 *             xfpga_fpgaUnregisterEvent() returns FPGA_INVALID_PARAM.
 *
 */
TEST_P(events_mock_p, event_drv_14) {
  fpga_event_handle bad_handle;
  EXPECT_EQ(FPGA_OK, xfpga_fpgaCreateEventHandle(&bad_handle));

  // Valid magic and ioctl
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  EXPECT_EQ(FPGA_OK, xfpga_fpgaUnregisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT, bad_handle));

  // Destory event handle
  auto res = xfpga_fpgaDestroyEventHandle(&bad_handle);
  EXPECT_EQ(FPGA_OK, res);
}

/**
 * @test       send_fme_event_request
 * @brief      When passed a valid event handle but invalid fme params.
 *             It returns FPGA_INVALID_PARAM
 */
TEST_P(events_mock_p, invalid_fme_event_request_01){
  int fme_op = 0;
  auto res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  fme_op = FPGA_IRQ_ASSIGN;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, dummy_ioctl<-1,EINVAL>);

  res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  system_->register_ioctl_handler(FPGA_FME_ERR_SET_IRQ, dummy_ioctl<-1,EINVAL>);
  res = send_fme_event_request(handle_dev_,eh_,fme_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       send_port_event_request
 *
 * @brief      When passed a valid event handle but invalid port params.
 *             It returns FPGA_INVALID_PARAM
 *
 */
TEST_P(events_mock_p, invalid_port_event_request_01){
  int port_op = 0;
  auto res = send_port_event_request(handle_dev_,eh_,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  port_op = FPGA_IRQ_ASSIGN;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, dummy_ioctl<-1,EINVAL>);

  res = send_port_event_request(handle_dev_,eh_,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  system_->register_ioctl_handler(FPGA_PORT_ERR_SET_IRQ, dummy_ioctl<-1,EINVAL>);
  res = send_port_event_request(handle_dev_,eh_,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       send_uafu_event_request
 * @brief      When passed a valid event handle but invalid port params.
 *             It returns FPGA_INVALID_PARAM. When ioctl fails,
 *             it returns FPGA_EXCEPTION.
 */
TEST_P(events_mock_p, invalid_uafu_event_request_01){
  // invalid port operation
  int port_op = 0;
  auto res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  // Invalid ioctl
  port_op = FPGA_IRQ_ASSIGN;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, dummy_ioctl<-1,EINVAL>);
  res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);
}

/**
 * @test       send_uafu_event_request
 * @brief      When passed a valid event handle, handle and port params.
 *             It returns FPGA_EXCEPTION when ioctl fails.
 */
TEST_P(events_mock_p, invalid_uafu_event_request_02){
  int port_op = FPGA_IRQ_ASSIGN;
  auto res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res) << "\t result is " << res;

  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);

  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  system_->register_ioctl_handler(FPGA_PORT_UAFU_SET_IRQ, dummy_ioctl<-1,EINVAL>);
  res = send_uafu_event_request(handle_dev_,eh_,0,port_op);
  EXPECT_EQ(FPGA_EXCEPTION,res);
}

/**
 * @test       fme_interrupts_check
 * @brief      When irq is not supported and register invalid ioctl,
 *             check_interrupts_supported returns FPGA_NOT_SUPPORTED 
 *             or FPGA_EXCEPTION.
 */
TEST_P(events_mock_p, fme_interrupts_check){
  fpga_objtype obj;
  auto h = (struct _fpga_handle*)handle_dev_;
  auto t = (struct _fpga_token*)h->token;

  // no fme_info capability
  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  auto res = check_interrupts_supported(handle_dev_,&obj);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);

  // Value input
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  res = check_interrupts_supported(handle_dev_,&obj);
  EXPECT_EQ(FPGA_OK,res);

  // interrupt ioctl
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, dummy_ioctl<-1,EINVAL>);
  res = check_interrupts_supported(handle_dev_,&obj);
  EXPECT_EQ(FPGA_EXCEPTION,res);

  // change sysfspath
  strncpy(t->sysfspath, "null" , 5);
  res = check_interrupts_supported(handle_dev_,&obj);
  EXPECT_NE(FPGA_OK,res);
}

/**
 * @test       afu_interrupts_check
 * @brief      When irq is not supported and register invalid ioctl,
 *             check_interrupts_supported returns FPGA_NOT_SUPPORTED 
 *             or FPGA_EXCEPTION.
 */
TEST_P(events_mock_p, afu_interrupts_check){
  fpga_objtype obj;
  auto h = (struct _fpga_handle*)handle_accel_;
  auto t = (struct _fpga_token*)h->token;

  // no fme_info capability
  gEnableIRQ = false;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  auto res = check_interrupts_supported(handle_accel_,&obj);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);

  // Value input
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  res = check_interrupts_supported(handle_accel_,&obj);
  EXPECT_EQ(FPGA_OK,res);

  // interrupt ioctl
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, dummy_ioctl<-1,EINVAL>);
  res = check_interrupts_supported(handle_accel_,&obj);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  // change sysfspath
  strncpy(t->sysfspath, "null", 5);
  res = check_interrupts_supported(handle_accel_,&obj);
  EXPECT_NE(FPGA_OK,res);
}

/**
 * @test       afu_driver_unregister_event
 * @brief      Given invalid ioctls, driver_unregister_event returns
 *             FPGA_INVALID_PARAM.
 */
TEST_P(events_mock_p, afu_driver_unregister_event){
  // Invalid ioctl
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  system_->register_ioctl_handler(FPGA_PORT_ERR_SET_IRQ, dummy_ioctl<-1,EINVAL>);
  auto res = driver_unregister_event(handle_accel_, FPGA_EVENT_ERROR, eh_);
  EXPECT_EQ(FPGA_INVALID_PARAM,res);

  system_->register_ioctl_handler(FPGA_PORT_UAFU_SET_IRQ, dummy_ioctl<-1,EINVAL>);
  res = driver_unregister_event(handle_accel_, FPGA_EVENT_INTERRUPT, eh_);
  EXPECT_EQ(FPGA_EXCEPTION,res);

  // Not supported event_type
  res = driver_unregister_event(handle_accel_, FPGA_EVENT_POWER_THERMAL, eh_);
  EXPECT_EQ(FPGA_NOT_SUPPORTED,res);
}

/**
 * @test       irq_event_01
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called for<br>
 *             an FPGA_DEVICE and FPGA_EVENT_ERROR<br>
 *             then the call is successful and<br>
 *             we can receive interrupt events on<br>
 *             the OS-specific object from the event handle.<br>
 */
TEST_P(events_mock_p, irq_event_01) {
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_FME_GET_INFO, fme_info);
  system_->register_ioctl_handler(FPGA_FME_ERR_SET_IRQ, set_fme_irq);
 
  ASSERT_EQ(FPGA_OK, xfpga_fpgaRegisterEvent(handle_dev_, FPGA_EVENT_ERROR,
                                       eh_, 0));
  int res;
  int fd = -1;

  EXPECT_EQ(FPGA_OK, xfpga_fpgaGetOSObjectFromEventHandle(eh_, &fd));
  EXPECT_GE(fd, 0);

  struct pollfd poll_fd;
  int maxpolls = 20;

  poll_fd.fd      = fd;
  poll_fd.events  = POLLIN | POLLPRI;
  poll_fd.revents = 0;

  do
  {
    res = poll(&poll_fd, 1, 1000);
    ASSERT_GE(res, 0);
    --maxpolls;
    ASSERT_GT(maxpolls, 0);
  } while(res == 0);

  EXPECT_EQ(res, 1);
  EXPECT_NE(poll_fd.revents, 0);

  EXPECT_EQ(FPGA_OK, xfpga_fpgaUnregisterEvent(handle_dev_, FPGA_EVENT_ERROR,
                                         eh_));
}

/**
 * @test       irq_event_02
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called for<br>
 *             an FPGA_ACCELERATOR and FPGA_EVENT_ERROR<br>
 *             then the call is successful and<br>
 *             we can receive interrupt events on<br>
 *             the OS-specific object from the event handle.<br>
 *
 */
TEST_P(events_mock_p, irq_event_02) {
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  system_->register_ioctl_handler(FPGA_PORT_ERR_SET_IRQ, set_port_irq);
 
  ASSERT_EQ(FPGA_OK, xfpga_fpgaRegisterEvent(handle_accel_, FPGA_EVENT_ERROR, eh_, 0));

  int res;
  int fd = -1;

  EXPECT_EQ(FPGA_OK, xfpga_fpgaGetOSObjectFromEventHandle(eh_, &fd));
  EXPECT_GE(fd, 0);

  struct pollfd poll_fd;
  int maxpolls = 20;

  poll_fd.fd      = fd;
  poll_fd.events  = POLLIN | POLLPRI;
  poll_fd.revents = 0;

  do
  {
    res = poll(&poll_fd, 1, 1000);
    ASSERT_GE(res, 0);
    --maxpolls;
    ASSERT_GT(maxpolls, 0);
  } while(res == 0);

  EXPECT_EQ(res, 1);
  EXPECT_NE(poll_fd.revents, 0);

  EXPECT_EQ(FPGA_OK, xfpga_fpgaUnregisterEvent(handle_accel_, FPGA_EVENT_ERROR, eh_));
}

/**
 * @test       irq_event_03
 *
 * @brief      Given a driver with IRQ support<br>
 *             when fpgaRegisterEvent is called for<br>
 *             an FPGA_ACCELERATOR and FPGA_EVENT_INTERRUPT<br>
 *             then the call is successful and<br>
 *             we can receive interrupt events on<br>
 *             the OS-specific object from the event handle.<br>
 *
 */
TEST_P(events_mock_p, irq_event_03) {
  gEnableIRQ = true;
  system_->register_ioctl_handler(FPGA_PORT_GET_INFO, port_info);
  system_->register_ioctl_handler(FPGA_PORT_UAFU_SET_IRQ, set_uport_irq);
 
  ASSERT_EQ(FPGA_OK, xfpga_fpgaRegisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT,
                                       eh_, 0));

  int res;
  int fd = -1;

  EXPECT_EQ(FPGA_OK, xfpga_fpgaGetOSObjectFromEventHandle(eh_, &fd));
  EXPECT_GE(fd, 0);

  struct pollfd poll_fd;
  int maxpolls = 20;

  poll_fd.fd      = fd;
  poll_fd.events  = POLLIN | POLLPRI;
  poll_fd.revents = 0;

  do
  {
    res = poll(&poll_fd, 1, 1000);
    ASSERT_GE(res, 0);
    --maxpolls;
    ASSERT_GT(maxpolls, 0);
  } while(res == 0);

  EXPECT_EQ(res, 1);
  EXPECT_NE(poll_fd.revents, 0);

  EXPECT_EQ(FPGA_OK, xfpga_fpgaUnregisterEvent(handle_accel_, FPGA_EVENT_INTERRUPT,
                                         eh_));
}

INSTANTIATE_TEST_CASE_P(events, events_mock_p,
                        ::testing::ValuesIn(test_platform::mock_platforms({ "skx-p","dcp-rc" })));
