#include <gtest/gtest.h>

using namespace std;

extern "C" {
  #include "pa_api.h"
  #include "xattr.h"
}

#include <string.h>
#include <string>

class Xattr : public ::testing::Test {
 public:
  const string file_name_ = "file_with_xattr";
  static const int correct_fd_ = 5;
  static string correct_attr_name_;
  static string wrong_attr_name_;
  uint8_t xattr_val_[3] = {0x1, 0x2, 0x3};
  static size_t xattr_size_;

  uint8_t buffer_[100];
  size_t xattr_size_in_;
  int xattr_fd_ = 5;

  void SetUp() {
    xattr_fd_ = correct_fd_;
    correct_attr_name_ = "pa";
    wrong_attr_name_ = "wrong_attr";
    xattr_size_ = sizeof(xattr_val_);
  }

  void TearDown() {
  }
};

string Xattr::correct_attr_name_ = "";
string Xattr::wrong_attr_name_ = "";
size_t Xattr::xattr_size_ = 0;

extern "C" ssize_t getxattr (const char *__path, const char *__name,
                             void *__value, size_t __size) {
  size_t xattr_size = Xattr::xattr_size_;
  if (0 == strcmp(__name, Xattr::correct_attr_name_.c_str())) {
    return xattr_size;
  }
  return (-1);
}

extern "C" int setxattr (const char *__path, const char *__name,
                         const void *__value, size_t __size, int __flags) {
  if (0 == strcmp(__name, Xattr::correct_attr_name_.c_str())) {
    return 0;
  }
  return (-1);
}

extern "C" ssize_t fgetxattr (int __fd, const char *__name, void *__value,
                              size_t __size) {
  if (0 == strcmp(__name, Xattr::correct_attr_name_.c_str())) {
    return Xattr::xattr_size_;
  }
  return (-1);
}

extern "C" int fsetxattr (int __fd, const char *__name, const void *__value,
                          size_t __size, int __flags) {
  if (0 == strcmp(__name, Xattr::correct_attr_name_.c_str())) {
    return 0;
  }
  return (-1);
}

extern "C" int fcntl(int __fd, int cmd, long arg) {
  return ((__fd == Xattr::correct_fd_) ? 0 : 1);
}

TEST_F(Xattr, XattrHasAttr_WrongInputParams) {
  size_t xattr_size;
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrHasAttr(NULL, correct_attr_name_.c_str(), &xattr_size));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrHasAttr(file_name_.c_str(), NULL, &xattr_size));
}

TEST_F(Xattr, XattrHasAttr_WrongXattrName) {
  size_t xattr_size;
  ASSERT_EQ(PA_GENERAL_ERROR, XattrHasAttr(file_name_.c_str(), wrong_attr_name_.c_str(), &xattr_size));
}

TEST_F(Xattr, XattrHasAttr_NoValueSize) {
  ASSERT_EQ(PA_SUCCESS, XattrHasAttr(file_name_.c_str(), correct_attr_name_.c_str(), NULL));
}

TEST_F(Xattr, XattrHasAttr_PaSuccess) {
  size_t xattr_size;
  ASSERT_EQ(PA_SUCCESS, XattrHasAttr(file_name_.c_str(), correct_attr_name_.c_str(), &xattr_size));
}

TEST_F(Xattr, XattrRead_WrongInputParams) {
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrRead(NULL, correct_attr_name_.c_str(), (void**)&buffer_, &xattr_size_in_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrRead(file_name_.c_str(), NULL, (void**)&buffer_, &xattr_size_in_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrRead(file_name_.c_str(), correct_attr_name_.c_str(), NULL, &xattr_size_in_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrRead(file_name_.c_str(), correct_attr_name_.c_str(), (void**)&buffer_, NULL));
}

TEST_F(Xattr, XattrRead_WrongXattrName) {
  ASSERT_EQ(PA_GENERAL_ERROR, XattrRead(file_name_.c_str(), wrong_attr_name_.c_str(), (void**)&buffer_, &xattr_size_in_));
}

TEST_F(Xattr, XattrRead_PaSuccess) {
  ASSERT_EQ(PA_SUCCESS, XattrRead(file_name_.c_str(), correct_attr_name_.c_str(), (void**)&buffer_, &xattr_size_in_));
}

TEST_F(Xattr, XattrWrite_WrongInputParams) {
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrWrite(NULL, correct_attr_name_.c_str(), (void*)xattr_val_, xattr_size_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrWrite(file_name_.c_str(), NULL, (void*)xattr_val_, xattr_size_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrWrite(file_name_.c_str(), correct_attr_name_.c_str(), NULL, xattr_size_));
}

TEST_F(Xattr, XattrWrite_WrongXattrName) {
  ASSERT_EQ(PA_GENERAL_ERROR, XattrWrite(file_name_.c_str(), wrong_attr_name_.c_str(), (void*)xattr_val_, xattr_size_));
}

TEST_F(Xattr, XattrWrite_PaSuccess) {
  ASSERT_EQ(PA_SUCCESS, XattrWrite(file_name_.c_str(), correct_attr_name_.c_str(), (void*)xattr_val_, xattr_size_));
}

TEST_F(Xattr, XattrFdHasAttr_WrongInputParams) {
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrFdHasAttr(xattr_fd_, NULL, &xattr_size_in_));
}

TEST_F(Xattr, XattrFdHasAttr_PaSuccess) {
  ASSERT_EQ(PA_SUCCESS, XattrFdHasAttr(xattr_fd_, correct_attr_name_.c_str(), &xattr_size_in_));
}

TEST_F(Xattr, XattrFdWrite_WrongInputParams) {
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrFdWrite(xattr_fd_, NULL, (void*)xattr_val_, xattr_size_));
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrFdWrite(xattr_fd_, correct_attr_name_.c_str(), NULL, xattr_size_));
}

TEST_F(Xattr, XattrFdWrite_WrongXattrName) {
  ASSERT_EQ(PA_GENERAL_ERROR, XattrFdWrite(xattr_fd_, wrong_attr_name_.c_str(), (void*)xattr_val_, xattr_size_));
}

TEST_F(Xattr, XattrFdWrite_PaSuccess) {
  ASSERT_EQ(PA_SUCCESS, XattrFdWrite(xattr_fd_, correct_attr_name_.c_str(), (void*)xattr_val_, xattr_size_));
}

TEST_F(Xattr, XattrUserPaFcntl_WrongInputParams_InvalidArguments) {
  ASSERT_EQ(PA_INVALID_ARGUMENTS, XattrUserPaFcntl(xattr_fd_, NULL, xattr_size_));
}

TEST_F(Xattr, XattrUserPaFcntl_HugeSize_MallocError) {
  ASSERT_EQ(PA_MALLOC_ERROR, XattrUserPaFcntl(xattr_fd_, (void*)xattr_val_, (size_t)(-8)));
}

TEST_F(Xattr, XattrUserPaFcntl_FdIsBad_Failed) {
  ASSERT_EQ(PA_GENERAL_ERROR, XattrUserPaFcntl(Xattr::correct_fd_ + 1, (void*)xattr_val_, xattr_size_));
}

TEST_F(Xattr, XattrUserPaFcntl_PaSuccess) {
  ASSERT_EQ(PA_SUCCESS, XattrUserPaFcntl(xattr_fd_, (void*)xattr_val_, xattr_size_));
}
