<commit_before>#include <xzero/executor/Executor.h>
#include <xzero/net/InetConnector.h>
#include <xzero/net/InetEndPoint.h>
#include <xzero/net/ConnectionFactory.h>
#include <xzero/net/Connection.h>
#include <xzero/net/IPAddress.h>
#include <xzero/io/SelectionKey.h>
#include <xzero/sysconfig.h>
#include <algorithm>
#include <mutex>
#include <system_error>

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>

#if defined(SD_FOUND)
#include <systemd/sd-daemon.h>
#endif

#if !defined(SO_REUSEPORT)
#define SO_REUSEPORT 15
#endif

#if 0 // !defined(NDEBUG)
static std::mutex m;
#define TRACE(msg...)  do { \
    m.lock(); \
    printf("InetConnector: " msg); \
    printf("\n"); \
    m.unlock(); \
  } while (0);
#else
#define TRACE(msg...) do { } while (0)
#endif

namespace xzero {

InetConnector::InetConnector(const std::string& name, Executor* executor,
                             Scheduler* scheduler, Selector* selector,
                             WallClock* clock)
    : Connector(name, executor, clock),
      selector_(selector),
      scheduler_(scheduler),
      connectedEndPoints_(),
      socket_(-1),
      addressFamily_(IPAddress::V4),
      typeMask_(0),
      flags_(0),
      backlog_(256),
      multiAcceptCount_(1),
      isStarted_(false) {
}

InetConnector::InetConnector(const std::string& name, Executor* executor,
                             Scheduler* scheduler, Selector* selector,
                             WallClock* clock,
                             const IPAddress& ipaddress, int port, int backlog,
                             bool reuseAddr, bool reusePort)
    : InetConnector(name, executor, scheduler, selector, clock) {

  open(ipaddress, port, backlog, reuseAddr, reusePort);
}

void InetConnector::open(const IPAddress& ipaddress, int port, int backlog,
                         bool reuseAddr, bool reusePort) {
  if (isOpen())
    throw std::runtime_error("InetConnector already opened");

  socket_ = ::socket(ipaddress.family(), SOCK_STREAM, 0);
  addressFamily_ = ipaddress.family();

  if (socket_ < 0)
    throw std::system_error(errno, std::system_category());

  if (reusePort)
    setReusePort(reusePort);

  if (reuseAddr)
    setReuseAddr(reuseAddr);

  bind(ipaddress, port);
}

void InetConnector::close() {
  if (isStarted()) {
    stop();
  }

  if (isOpen()) {
    ::close(socket_);
    socket_ = -1;
  }
}

void InetConnector::bind(const IPAddress& ipaddr, int port) {
  char sa[sizeof(sockaddr_in6)];
  socklen_t salen = ipaddr.size();

  switch (ipaddr.family()) {
    case IPAddress::V4:
      salen = sizeof(sockaddr_in);
      ((sockaddr_in*)sa)->sin_port = htons(port);
      ((sockaddr_in*)sa)->sin_family = AF_INET;
      memcpy(&((sockaddr_in*)sa)->sin_addr, ipaddr.data(), ipaddr.size());
      break;
    case IPAddress::V6:
      salen = sizeof(sockaddr_in6);
      ((sockaddr_in6*)sa)->sin6_port = htons(port);
      ((sockaddr_in6*)sa)->sin6_family = AF_INET6;
      memcpy(&((sockaddr_in6*)sa)->sin6_addr, ipaddr.data(), ipaddr.size());
      break;
    default:
      throw std::system_error(EINVAL, std::system_category());
  }

  int rv = ::bind(socket_, (sockaddr*)sa, salen);
  if (rv < 0)
    throw std::system_error(errno, std::system_category());

  addressFamily_ = ipaddr.family();
}

void InetConnector::listen(int backlog) {
  int rv = ::listen(socket_, backlog);
  if (rv < 0)
    throw std::system_error(errno, std::system_category());
}

bool InetConnector::isOpen() const noexcept {
  return socket_ >= 0;
}

InetConnector::~InetConnector() {
  TRACE("~InetConnector");
  if (isStarted()) {
    stop();
  }

  if (socket_ >= 0) {
    ::close(socket_);
  }
}

Selector* InetConnector::selector() const noexcept {
  return selector_;
}

int InetConnector::handle() const noexcept {
  return socket_;
}

void InetConnector::setSocket(int socket) {
  socket_ = socket;
}

size_t InetConnector::backlog() const noexcept {
  return backlog_;
}

void InetConnector::setBacklog(size_t value) {
  if (isStarted()) {
    throw std::runtime_error("Cannot change backlog when already listening.");
  }

  backlog_ = value;
}

bool InetConnector::isBlocking() const {
  return !(fcntl(socket_, F_GETFL) & O_NONBLOCK);
}

void InetConnector::setBlocking(bool enable) {
  unsigned flags = enable ? fcntl(socket_, F_GETFL) & ~O_NONBLOCK
                          : fcntl(socket_, F_GETFL) | O_NONBLOCK;

  if (fcntl(socket_, F_SETFL, flags) < 0) {
    throw std::system_error(errno, std::system_category());
  }

#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  if (enable) {
    typeMask_ |= SOCK_NONBLOCK;
  } else {
    typeMask_ &= ~SOCK_NONBLOCK;
  }
#else
  if (enable) {
    flags_ |= O_NONBLOCK;
  } else {
    flags_ &= ~O_NONBLOCK;
  }
#endif
}

bool InetConnector::closeOnExec() const {
  return fcntl(socket_, F_GETFD) & FD_CLOEXEC;
}

void InetConnector::setCloseOnExec(bool enable) {
  unsigned flags = enable ? fcntl(socket_, F_GETFD) | FD_CLOEXEC
                          : fcntl(socket_, F_GETFD) & ~FD_CLOEXEC;

  if (fcntl(socket_, F_SETFD, flags) < 0) {
    throw std::system_error(errno, std::system_category());
  }

#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  if (enable) {
    typeMask_ |= SOCK_CLOEXEC;
  } else {
    typeMask_ &= ~SOCK_CLOEXEC;
  }
#else
  if (enable) {
    flags_ |= O_CLOEXEC;
  } else {
    flags_ &= ~O_CLOEXEC;
  }
#endif
}

bool InetConnector::deferAccept() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_TCP, TCP_DEFER_ACCEPT, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setDeferAccept(bool enable) {
#if defined(TCP_DEFER_ACCEPT) && defined(ENABLE_TCP_DEFER_ACCEPT)
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_TCP, TCP_DEFER_ACCEPT, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
#else
  throw std::system_error(ENOTSUP, std::system_category());
#endif
}

bool InetConnector::quickAck() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_TCP, TCP_QUICKACK, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setQuickAck(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_TCP, TCP_QUICKACK, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

bool InetConnector::reusePort() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setReusePort(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

bool InetConnector::reuseAddr() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setReuseAddr(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

size_t InetConnector::multiAcceptCount() const noexcept {
  return multiAcceptCount_;
}

void InetConnector::setMultiAcceptCount(size_t value) noexcept {
  multiAcceptCount_ = value;
}

void InetConnector::start() {
  if (!isOpen()) {
    throw std::runtime_error("Connector must be open in order to be started.");
  }

  if (isStarted()) {
    return;
  }

  listen(backlog_);

  isStarted_ = true;

  if (selector_)
    selectionKey_ = selector_->createSelectable(this, READ);
  else {
    executor()->execute([this]() {
      while (isStarted_) {
        acceptOne();
      }
    });
  }
}

bool InetConnector::isStarted() const noexcept {
  return isStarted_;
}

void InetConnector::stop() {
  TRACE("stop: %s", name().c_str());
  if (!isStarted()) {
    return;
  }

  selectionKey_.reset();
  isStarted_ = false;
}

void InetConnector::onSelectable() noexcept {
  try {
    for (size_t i = 0; i < multiAcceptCount_; i++) {
      if (!acceptOne()) {
        break;
      }
    }
  } catch (const std::exception& e) {
    // TODO
  } catch (...) {
    // TODO
  }
}

bool InetConnector::acceptOne() {
#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  bool flagged = true;
  int cfd = ::accept4(socket_, nullptr, 0, typeMask_);
  if (cfd < 0 && errno == ENOSYS) {
    cfd = ::accept(socket_, nullptr, 0);
    flagged = false;
  }
#else
  bool flagged = false;
  int cfd = ::accept(socket_, nullptr, 0);
#endif

  if (cfd < 0) {
    switch (errno) {
      case EINTR:
      case EAGAIN:
#if EAGAIN != EWOULDBLOCK
      case EWOULDBLOCK:
#endif
        return true;
      default:
        throw std::system_error(errno, std::system_category());
    }
  }

  if (!flagged && flags_ &&
      fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL) | flags_) < 0) {
    ::close(cfd);
    throw std::system_error(errno, std::system_category());
  }

  std::unique_ptr<InetEndPoint> endpoint(new InetEndPoint(cfd, this));
  connectedEndPoints_.push_back(endpoint.get());

  auto connection =
      defaultConnectionFactory()->create(this, std::move(endpoint));
  connection->onOpen();

  return true;
}

std::list<EndPoint*> InetConnector::connectedEndPoints() {
  std::list<EndPoint*> result;
  for (InetEndPoint* ep : connectedEndPoints_) {
    result.push_back(ep);
  }
  return result;
}

void InetConnector::onEndPointClosed(InetEndPoint* endpoint) {
  assert(endpoint != nullptr);
  assert(endpoint->connection() != nullptr);

  auto i = std::find(connectedEndPoints_.begin(), connectedEndPoints_.end(),
                     endpoint);

  assert(i != connectedEndPoints_.end());

  if (i != connectedEndPoints_.end()) {
    endpoint->connection()->onClose();

    connectedEndPoints_.erase(i);

    if (!endpoint->isBusy()) {
      // do only actually delete if not currently inside an io handler
      release(endpoint->connection());
    }
  }
}

void InetConnector::release(Connection* inetConnection) {
  assert(inetConnection != nullptr);
  assert(dynamic_cast<InetEndPoint*>(inetConnection->endpoint()) != nullptr);
  assert(!static_cast<InetEndPoint*>(inetConnection->endpoint())->isBusy());

  delete inetConnection;
}

}  // namespace xzero
<commit_msg>InetConnector blocking/non-blocking fix<commit_after>#include <xzero/executor/Executor.h>
#include <xzero/net/InetConnector.h>
#include <xzero/net/InetEndPoint.h>
#include <xzero/net/ConnectionFactory.h>
#include <xzero/net/Connection.h>
#include <xzero/net/IPAddress.h>
#include <xzero/io/SelectionKey.h>
#include <xzero/sysconfig.h>
#include <algorithm>
#include <mutex>
#include <system_error>

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>

#if defined(SD_FOUND)
#include <systemd/sd-daemon.h>
#endif

#if !defined(SO_REUSEPORT)
#define SO_REUSEPORT 15
#endif

#if 0 // !defined(NDEBUG)
static std::mutex m;
#define TRACE(msg...)  do { \
    m.lock(); \
    printf("InetConnector: " msg); \
    printf("\n"); \
    m.unlock(); \
  } while (0);
#else
#define TRACE(msg...) do { } while (0)
#endif

namespace xzero {

InetConnector::InetConnector(const std::string& name, Executor* executor,
                             Scheduler* scheduler, Selector* selector,
                             WallClock* clock)
    : Connector(name, executor, clock),
      selector_(selector),
      scheduler_(scheduler),
      connectedEndPoints_(),
      socket_(-1),
      addressFamily_(IPAddress::V4),
      typeMask_(0),
      flags_(0),
      backlog_(256),
      multiAcceptCount_(1),
      isStarted_(false) {
}

InetConnector::InetConnector(const std::string& name, Executor* executor,
                             Scheduler* scheduler, Selector* selector,
                             WallClock* clock,
                             const IPAddress& ipaddress, int port, int backlog,
                             bool reuseAddr, bool reusePort)
    : InetConnector(name, executor, scheduler, selector, clock) {

  open(ipaddress, port, backlog, reuseAddr, reusePort);
}

void InetConnector::open(const IPAddress& ipaddress, int port, int backlog,
                         bool reuseAddr, bool reusePort) {
  if (isOpen())
    throw std::runtime_error("InetConnector already opened");

  socket_ = ::socket(ipaddress.family(), SOCK_STREAM, 0);
  addressFamily_ = ipaddress.family();

  if (socket_ < 0)
    throw std::system_error(errno, std::system_category());

  if (reusePort)
    setReusePort(reusePort);

  if (reuseAddr)
    setReuseAddr(reuseAddr);

  bind(ipaddress, port);
}

void InetConnector::close() {
  if (isStarted()) {
    stop();
  }

  if (isOpen()) {
    ::close(socket_);
    socket_ = -1;
  }
}

void InetConnector::bind(const IPAddress& ipaddr, int port) {
  char sa[sizeof(sockaddr_in6)];
  socklen_t salen = ipaddr.size();

  switch (ipaddr.family()) {
    case IPAddress::V4:
      salen = sizeof(sockaddr_in);
      ((sockaddr_in*)sa)->sin_port = htons(port);
      ((sockaddr_in*)sa)->sin_family = AF_INET;
      memcpy(&((sockaddr_in*)sa)->sin_addr, ipaddr.data(), ipaddr.size());
      break;
    case IPAddress::V6:
      salen = sizeof(sockaddr_in6);
      ((sockaddr_in6*)sa)->sin6_port = htons(port);
      ((sockaddr_in6*)sa)->sin6_family = AF_INET6;
      memcpy(&((sockaddr_in6*)sa)->sin6_addr, ipaddr.data(), ipaddr.size());
      break;
    default:
      throw std::system_error(EINVAL, std::system_category());
  }

  int rv = ::bind(socket_, (sockaddr*)sa, salen);
  if (rv < 0)
    throw std::system_error(errno, std::system_category());

  addressFamily_ = ipaddr.family();
}

void InetConnector::listen(int backlog) {
  int rv = ::listen(socket_, backlog);
  if (rv < 0)
    throw std::system_error(errno, std::system_category());
}

bool InetConnector::isOpen() const noexcept {
  return socket_ >= 0;
}

InetConnector::~InetConnector() {
  TRACE("~InetConnector");
  if (isStarted()) {
    stop();
  }

  if (socket_ >= 0) {
    ::close(socket_);
  }
}

Selector* InetConnector::selector() const noexcept {
  return selector_;
}

int InetConnector::handle() const noexcept {
  return socket_;
}

void InetConnector::setSocket(int socket) {
  socket_ = socket;
}

size_t InetConnector::backlog() const noexcept {
  return backlog_;
}

void InetConnector::setBacklog(size_t value) {
  if (isStarted()) {
    throw std::runtime_error("Cannot change backlog when already listening.");
  }

  backlog_ = value;
}

bool InetConnector::isBlocking() const {
  return !(fcntl(socket_, F_GETFL) & O_NONBLOCK);
}

void InetConnector::setBlocking(bool enable) {
  unsigned flags = enable ? fcntl(socket_, F_GETFL) & ~O_NONBLOCK
                          : fcntl(socket_, F_GETFL) | O_NONBLOCK;

  if (fcntl(socket_, F_SETFL, flags) < 0) {
    throw std::system_error(errno, std::system_category());
  }

#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  if (enable) {
    typeMask_ &= ~SOCK_NONBLOCK;
  } else {
    typeMask_ |= SOCK_NONBLOCK;
  }
#else
  if (enable) {
    flags_ &= ~O_NONBLOCK;
  } else {
    flags_ |= O_NONBLOCK;
  }
#endif
}

bool InetConnector::closeOnExec() const {
  return fcntl(socket_, F_GETFD) & FD_CLOEXEC;
}

void InetConnector::setCloseOnExec(bool enable) {
  unsigned flags = enable ? fcntl(socket_, F_GETFD) | FD_CLOEXEC
                          : fcntl(socket_, F_GETFD) & ~FD_CLOEXEC;

  if (fcntl(socket_, F_SETFD, flags) < 0) {
    throw std::system_error(errno, std::system_category());
  }

#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  if (enable) {
    typeMask_ |= SOCK_CLOEXEC;
  } else {
    typeMask_ &= ~SOCK_CLOEXEC;
  }
#else
  if (enable) {
    flags_ |= O_CLOEXEC;
  } else {
    flags_ &= ~O_CLOEXEC;
  }
#endif
}

bool InetConnector::deferAccept() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_TCP, TCP_DEFER_ACCEPT, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setDeferAccept(bool enable) {
#if defined(TCP_DEFER_ACCEPT) && defined(ENABLE_TCP_DEFER_ACCEPT)
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_TCP, TCP_DEFER_ACCEPT, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
#else
  throw std::system_error(ENOTSUP, std::system_category());
#endif
}

bool InetConnector::quickAck() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_TCP, TCP_QUICKACK, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setQuickAck(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_TCP, TCP_QUICKACK, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

bool InetConnector::reusePort() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setReusePort(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

bool InetConnector::reuseAddr() const {
  int optval = 1;
  socklen_t optlen = sizeof(optval);
  return ::getsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &optval, &optlen) == 0
             ? optval != 0
             : false;
}

void InetConnector::setReuseAddr(bool enable) {
  int rc = enable ? 1 : 0;
  if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0) {
    throw std::system_error(errno, std::system_category());
  }
}

size_t InetConnector::multiAcceptCount() const noexcept {
  return multiAcceptCount_;
}

void InetConnector::setMultiAcceptCount(size_t value) noexcept {
  multiAcceptCount_ = value;
}

void InetConnector::start() {
  if (!isOpen()) {
    throw std::runtime_error("Connector must be open in order to be started.");
  }

  if (isStarted()) {
    return;
  }

  listen(backlog_);

  isStarted_ = true;

  if (selector_)
    selectionKey_ = selector_->createSelectable(this, READ);
  else {
    executor()->execute([this]() {
      while (isStarted_) {
        acceptOne();
      }
    });
  }
}

bool InetConnector::isStarted() const noexcept {
  return isStarted_;
}

void InetConnector::stop() {
  TRACE("stop: %s", name().c_str());
  if (!isStarted()) {
    return;
  }

  selectionKey_.reset();
  isStarted_ = false;
}

void InetConnector::onSelectable() noexcept {
  try {
    for (size_t i = 0; i < multiAcceptCount_; i++) {
      if (!acceptOne()) {
        break;
      }
    }
  } catch (const std::exception& e) {
    // TODO
  } catch (...) {
    // TODO
  }
}

bool InetConnector::acceptOne() {
#if defined(HAVE_ACCEPT4) && defined(ENABLE_ACCEPT4)
  bool flagged = true;
  int cfd = ::accept4(socket_, nullptr, 0, typeMask_);
  if (cfd < 0 && errno == ENOSYS) {
    cfd = ::accept(socket_, nullptr, 0);
    flagged = false;
  }
#else
  bool flagged = false;
  int cfd = ::accept(socket_, nullptr, 0);
#endif

  if (cfd < 0) {
    switch (errno) {
      case EINTR:
      case EAGAIN:
#if EAGAIN != EWOULDBLOCK
      case EWOULDBLOCK:
#endif
        return true;
      default:
        throw std::system_error(errno, std::system_category());
    }
  }

  if (!flagged && flags_ &&
      fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL) | flags_) < 0) {
    ::close(cfd);
    throw std::system_error(errno, std::system_category());
  }

  std::unique_ptr<InetEndPoint> endpoint(new InetEndPoint(cfd, this));
  connectedEndPoints_.push_back(endpoint.get());

  auto connection =
      defaultConnectionFactory()->create(this, std::move(endpoint));
  connection->onOpen();

  return true;
}

std::list<EndPoint*> InetConnector::connectedEndPoints() {
  std::list<EndPoint*> result;
  for (InetEndPoint* ep : connectedEndPoints_) {
    result.push_back(ep);
  }
  return result;
}

void InetConnector::onEndPointClosed(InetEndPoint* endpoint) {
  assert(endpoint != nullptr);
  assert(endpoint->connection() != nullptr);

  auto i = std::find(connectedEndPoints_.begin(), connectedEndPoints_.end(),
                     endpoint);

  assert(i != connectedEndPoints_.end());

  if (i != connectedEndPoints_.end()) {
    endpoint->connection()->onClose();

    connectedEndPoints_.erase(i);

    if (!endpoint->isBusy()) {
      // do only actually delete if not currently inside an io handler
      release(endpoint->connection());
    }
  }
}

void InetConnector::release(Connection* inetConnection) {
  assert(inetConnection != nullptr);
  assert(dynamic_cast<InetEndPoint*>(inetConnection->endpoint()) != nullptr);
  assert(!static_cast<InetEndPoint*>(inetConnection->endpoint())->isBusy());

  delete inetConnection;
}

}  // namespace xzero
<|endoftext|>


<commit_before>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2009-2014 The Regents of
the University of Michigan All rights reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

You may redistribute, use, and create derivate works of HOOMD-blue, in source
and binary forms, provided you abide by the following conditions:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer both in the code and
prominently in any materials provided with the distribution.

* 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.

* All publications and presentations based on HOOMD-blue, including any reports
or published results obtained, in whole or in part, with HOOMD-blue, will
acknowledge its use according to the terms posted at the time of submission on:
http://codeblue.umich.edu/hoomd-blue/citations.html

* Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
http://codeblue.umich.edu/hoomd-blue/

* Apart from the above required attributions, neither the name of the copyright
holder nor the names of HOOMD-blue's contributors may be used to endorse or
promote products derived from this software without specific prior written
permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

//! label the boost test module
#define BOOST_TEST_MODULE GridshiftCorrectionTests
#include "boost_utf_configure.h"

#include "HOOMDMath.h"
#include "VectorMath.h"
#include "System.h"
#include "ParticleData.h"

#include <math.h>
#include <boost/shared_ptr.hpp>

/*! \file test_gridshift_correct.cc
    \brief Unit tests for the ParticleData class in response to origin shifts
    \ingroup unit_tests
*/

//! boost test case to verify proper operation of ZeroMomentumUpdater
BOOST_AUTO_TEST_CASE( ParticleDataGridShiftGetMethods )
    {
    // create a simple particle data to test with
    boost::shared_ptr<SystemDefinition> sysdef(new SystemDefinition(3, BoxDim(10.0), 4));
    boost::shared_ptr<ParticleData> pdata = sysdef->getParticleData();
    BoxDim box = pdata->getBox();
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<Scalar4> h_vel(pdata->getVelocities(), access_location::host, access_mode::readwrite);

    h_pos.data[0].x = h_pos.data[0].y = h_pos.data[0].z = 0.0;
    h_pos.data[1].x = h_pos.data[1].y = h_pos.data[1].z = 1.0;
    }

    // compute a shift and apply it to all particles, and origin
    Scalar3 shift = make_scalar3(0.5,0.125,0.75);
    pdata->translateOrigin(shift);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    Scalar3 pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-0.0), tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-1.0), tol_small);

    int3 pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);

    // compute a shift that will shift the image of the box
    Scalar3 shift_img = make_scalar3(10.5,10.125,10.75);
    pdata->translateOrigin(shift_img);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift_img);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(pos.x-0.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.y-0.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.z-0.0, tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(pos.x-1.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.y-1.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.z-1.0, tol_small);

    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    }

BOOST_AUTO_TEST_CASE( ParticleDataGridShiftSetMethods )
    {
    // create a simple particle data to test with
    boost::shared_ptr<SystemDefinition> sysdef(new SystemDefinition(3, BoxDim(10.0), 4));
    boost::shared_ptr<ParticleData> pdata = sysdef->getParticleData();
    BoxDim box = pdata->getBox();
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<Scalar4> h_vel(pdata->getVelocities(), access_location::host, access_mode::readwrite);

    h_pos.data[0].x = h_pos.data[0].y = h_pos.data[0].z = 0.0;
    h_pos.data[1].x = h_pos.data[1].y = h_pos.data[1].z = 1.0;
    }

    // compute a shift that will shift the image of the box
    Scalar3 shift_img =  make_scalar3(10.5,10.125,10.75);
    pdata->translateOrigin(shift_img);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift_img);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    Scalar3 pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(pos.x-0.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.y-0.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.z-0.0, tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(pos.x-1.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.y-1.0, tol_small);
    MY_BOOST_CHECK_SMALL(pos.z-1.0, tol_small);

    int3 pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(1);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);

    //OK, now we set the positions using the particle data proxy
    Scalar3 new_pos0 = make_scalar3(0.1,0.5,0.7);
    pdata->setPosition(0,new_pos0);
    Scalar3 new_pos1 = make_scalar3(0.4,0.1,2.75);
    pdata->setPosition(1,new_pos1);

    Scalar3 ret_pos0 = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(ret_pos0.x-new_pos0.x, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos0.y-new_pos0.y, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos0.z-new_pos0.z, tol_small);

    Scalar3 ret_pos1 = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(ret_pos1.x-new_pos1.x, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos1.y-new_pos1.y, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos1.z-new_pos1.z, tol_small);

    //OK, now do the same with the images
    int3 new_img0 = make_int3(1,-5,7);
    pdata->setImage(0,new_img0);
    int3 new_img1 = make_int3(4,1,10);
    pdata->setImage(1,new_img1);

    int3 ret_img0 = pdata->getImage(0);
    BOOST_CHECK_EQUAL(ret_img0.x-new_img0.x, 0);
    BOOST_CHECK_EQUAL(ret_img0.y-new_img0.y, 0);
    BOOST_CHECK_EQUAL(ret_img0.z-new_img0.z, 0);

    int3 ret_img1 = pdata->getImage(1);
    BOOST_CHECK_EQUAL(ret_img1.x-new_img1.x, 0);
    BOOST_CHECK_EQUAL(ret_img1.y-new_img1.y, 0);
    BOOST_CHECK_EQUAL(ret_img1.z-new_img1.z, 0);
    }

#ifdef WIN32
#pragma warning( pop )
#endif
<commit_msg>Really fix compile error<commit_after>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2009-2014 The Regents of
the University of Michigan All rights reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

You may redistribute, use, and create derivate works of HOOMD-blue, in source
and binary forms, provided you abide by the following conditions:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer both in the code and
prominently in any materials provided with the distribution.

* 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.

* All publications and presentations based on HOOMD-blue, including any reports
or published results obtained, in whole or in part, with HOOMD-blue, will
acknowledge its use according to the terms posted at the time of submission on:
http://codeblue.umich.edu/hoomd-blue/citations.html

* Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
http://codeblue.umich.edu/hoomd-blue/

* Apart from the above required attributions, neither the name of the copyright
holder nor the names of HOOMD-blue's contributors may be used to endorse or
promote products derived from this software without specific prior written
permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

//! label the boost test module
#define BOOST_TEST_MODULE GridshiftCorrectionTests
#include "boost_utf_configure.h"

#include "HOOMDMath.h"
#include "VectorMath.h"
#include "System.h"
#include "ParticleData.h"

#include <math.h>
#include <boost/shared_ptr.hpp>

/*! \file test_gridshift_correct.cc
    \brief Unit tests for the ParticleData class in response to origin shifts
    \ingroup unit_tests
*/

//! boost test case to verify proper operation of ZeroMomentumUpdater
BOOST_AUTO_TEST_CASE( ParticleDataGridShiftGetMethods )
    {
    // create a simple particle data to test with
    boost::shared_ptr<SystemDefinition> sysdef(new SystemDefinition(3, BoxDim(10.0), 4));
    boost::shared_ptr<ParticleData> pdata = sysdef->getParticleData();
    BoxDim box = pdata->getBox();
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<Scalar4> h_vel(pdata->getVelocities(), access_location::host, access_mode::readwrite);

    h_pos.data[0].x = h_pos.data[0].y = h_pos.data[0].z = 0.0;
    h_pos.data[1].x = h_pos.data[1].y = h_pos.data[1].z = 1.0;
    }

    // compute a shift and apply it to all particles, and origin
    Scalar3 shift = make_scalar3(0.5,0.125,0.75);
    pdata->translateOrigin(shift);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    Scalar3 pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-0.0), tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-1.0), tol_small);

    int3 pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);

    // compute a shift that will shift the image of the box
    Scalar3 shift_img = make_scalar3(10.5,10.125,10.75);
    pdata->translateOrigin(shift_img);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift_img);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-0.0), tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-1.0), tol_small);

    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    }

BOOST_AUTO_TEST_CASE( ParticleDataGridShiftSetMethods )
    {
    // create a simple particle data to test with
    boost::shared_ptr<SystemDefinition> sysdef(new SystemDefinition(3, BoxDim(10.0), 4));
    boost::shared_ptr<ParticleData> pdata = sysdef->getParticleData();
    BoxDim box = pdata->getBox();
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<Scalar4> h_vel(pdata->getVelocities(), access_location::host, access_mode::readwrite);

    h_pos.data[0].x = h_pos.data[0].y = h_pos.data[0].z = 0.0;
    h_pos.data[1].x = h_pos.data[1].y = h_pos.data[1].z = 1.0;
    }

    // compute a shift that will shift the image of the box
    Scalar3 shift_img =  make_scalar3(10.5,10.125,10.75);
    pdata->translateOrigin(shift_img);
    {
    ArrayHandle<Scalar4> h_pos(pdata->getPositions(), access_location::host, access_mode::readwrite);
    ArrayHandle<int3> h_img(pdata->getImages(), access_location::host, access_mode::readwrite);

    for (unsigned int i = 0; i < pdata->getN(); i++)
        {
        // read in the current position and orientation
        Scalar4 pos_i = h_pos.data[i];
        vec3<Scalar> r_i = vec3<Scalar>(pos_i); // translation from local to global coordinates
        r_i += vec3<Scalar>(shift_img);
        h_pos.data[i] = vec_to_scalar4(r_i, pos_i.w);
        box.wrap(h_pos.data[i], h_img.data[i]);
        }
    }

    // check that the particle positions are still the original ones
    Scalar3 pos = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-0.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-0.0), tol_small);
    pos = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(Scalar(pos.x-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.y-1.0), tol_small);
    MY_BOOST_CHECK_SMALL(Scalar(pos.z-1.0), tol_small);

    int3 pimg = pdata->getImage(0);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);
    pimg = pdata->getImage(1);
    BOOST_CHECK_EQUAL(pimg.x, 0);
    BOOST_CHECK_EQUAL(pimg.y, 0);
    BOOST_CHECK_EQUAL(pimg.z, 0);

    //OK, now we set the positions using the particle data proxy
    Scalar3 new_pos0 = make_scalar3(0.1,0.5,0.7);
    pdata->setPosition(0,new_pos0);
    Scalar3 new_pos1 = make_scalar3(0.4,0.1,2.75);
    pdata->setPosition(1,new_pos1);

    Scalar3 ret_pos0 = pdata->getPosition(0);
    MY_BOOST_CHECK_SMALL(ret_pos0.x-new_pos0.x, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos0.y-new_pos0.y, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos0.z-new_pos0.z, tol_small);

    Scalar3 ret_pos1 = pdata->getPosition(1);
    MY_BOOST_CHECK_SMALL(ret_pos1.x-new_pos1.x, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos1.y-new_pos1.y, tol_small);
    MY_BOOST_CHECK_SMALL(ret_pos1.z-new_pos1.z, tol_small);

    //OK, now do the same with the images
    int3 new_img0 = make_int3(1,-5,7);
    pdata->setImage(0,new_img0);
    int3 new_img1 = make_int3(4,1,10);
    pdata->setImage(1,new_img1);

    int3 ret_img0 = pdata->getImage(0);
    BOOST_CHECK_EQUAL(ret_img0.x-new_img0.x, 0);
    BOOST_CHECK_EQUAL(ret_img0.y-new_img0.y, 0);
    BOOST_CHECK_EQUAL(ret_img0.z-new_img0.z, 0);

    int3 ret_img1 = pdata->getImage(1);
    BOOST_CHECK_EQUAL(ret_img1.x-new_img1.x, 0);
    BOOST_CHECK_EQUAL(ret_img1.y-new_img1.y, 0);
    BOOST_CHECK_EQUAL(ret_img1.z-new_img1.z, 0);
    }

#ifdef WIN32
#pragma warning( pop )
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <random.h>
#include <scheduler.h>
#include <util/time.h>

#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(scheduler_tests)

static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
{
    {
        boost::unique_lock<boost::mutex> lock(mutex);
        counter += delta;
    }
    std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
    if (rescheduleTime != noTime) {
        CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
        s.schedule(f, rescheduleTime);
    }
}

BOOST_AUTO_TEST_CASE(manythreads)
{
    // Stress test: hundreds of microsecond-scheduled tasks,
    // serviced by 10 threads.
    //
    // So... ten shared counters, which if all the tasks execute
    // properly will sum to the number of tasks done.
    // Each task adds or subtracts a random amount from one of the
    // counters, and then schedules another task 0-1000
    // microseconds in the future to subtract or add from
    // the counter -random_amount+1, so in the end the shared
    // counters should sum to the number of initial tasks performed.
    CScheduler microTasks;

    boost::mutex counterMutex[10];
    int counter[10] = { 0 };
    FastRandomContext rng{/* fDeterministic */ true};
    auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9]
    auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
    auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    std::chrono::system_clock::time_point now = start;
    std::chrono::system_clock::time_point first, last;
    size_t nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 0);

    for (int i = 0; i < 100; ++i) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }
    nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 100);
    BOOST_CHECK(first < last);
    BOOST_CHECK(last > now);

    // As soon as these are created they will start running and servicing the queue
    boost::thread_group microThreads;
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));

    UninterruptibleSleep(std::chrono::microseconds{600});
    now = std::chrono::system_clock::now();

    // More threads and more tasks:
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
    for (int i = 0; i < 100; i++) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }

    // Drain the task queue then exit threads
    microTasks.stop(true);
    microThreads.join_all(); // ... wait until all the threads are done

    int counterSum = 0;
    for (int i = 0; i < 10; i++) {
        BOOST_CHECK(counter[i] != 0);
        counterSum += counter[i];
    }
    BOOST_CHECK_EQUAL(counterSum, 200);
}

BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
{
    CScheduler scheduler;

    // each queue should be well ordered with respect to itself but not other queues
    SingleThreadedSchedulerClient queue1(&scheduler);
    SingleThreadedSchedulerClient queue2(&scheduler);

    // create more threads than queues
    // if the queues only permit execution of one task at once then
    // the extra threads should effectively be doing nothing
    // if they don't we'll get out of order behaviour
    boost::thread_group threads;
    for (int i = 0; i < 5; ++i) {
        threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
    }

    // these are not atomic, if SinglethreadedSchedulerClient prevents
    // parallel execution at the queue level no synchronization should be required here
    int counter1 = 0;
    int counter2 = 0;

    // just simply count up on each queue - if execution is properly ordered then
    // the callbacks should run in exactly the order in which they were enqueued
    for (int i = 0; i < 100; ++i) {
        queue1.AddToProcessQueue([i, &counter1]() {
            bool expectation = i == counter1++;
            assert(expectation);
        });

        queue2.AddToProcessQueue([i, &counter2]() {
            bool expectation = i == counter2++;
            assert(expectation);
        });
    }

    // finish up
    scheduler.stop(true);
    threads.join_all();

    BOOST_CHECK_EQUAL(counter1, 100);
    BOOST_CHECK_EQUAL(counter2, 100);
}

BOOST_AUTO_TEST_CASE(mockforward)
{
    CScheduler scheduler;

    int counter{0};
    CScheduler::Function dummy = [&counter]{counter++;};

    // schedule jobs for 2, 5 & 8 minutes into the future
    int64_t min_in_milli = 60*1000;
    scheduler.scheduleFromNow(dummy, 2*min_in_milli);
    scheduler.scheduleFromNow(dummy, 5*min_in_milli);
    scheduler.scheduleFromNow(dummy, 8*min_in_milli);

    // check taskQueue
    std::chrono::system_clock::time_point first, last;
    size_t num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 3ul);

    std::thread scheduler_thread([&]() { scheduler.serviceQueue(); });

    // bump the scheduler forward 5 minutes
    scheduler.MockForward(std::chrono::seconds(5*60));

    // ensure scheduler has chance to process all tasks queued for before 1 ms from now.
    scheduler.scheduleFromNow([&scheduler]{ scheduler.stop(false); }, 1);
    scheduler_thread.join();

    // check that the queue only has one job remaining
    num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 1ul);

    // check that the dummy function actually ran
    BOOST_CHECK_EQUAL(counter, 2);

    // check that the time of the remaining job has been updated
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
    // should be between 2 & 3 minutes from now
    BOOST_CHECK(delta > 2*60 && delta < 3*60);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>test: Check that wait_until returns if time point is in the past<commit_after>// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <random.h>
#include <scheduler.h>
#include <util/time.h>

#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(scheduler_tests)

static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
{
    {
        boost::unique_lock<boost::mutex> lock(mutex);
        counter += delta;
    }
    std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
    if (rescheduleTime != noTime) {
        CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
        s.schedule(f, rescheduleTime);
    }
}

BOOST_AUTO_TEST_CASE(manythreads)
{
    // Stress test: hundreds of microsecond-scheduled tasks,
    // serviced by 10 threads.
    //
    // So... ten shared counters, which if all the tasks execute
    // properly will sum to the number of tasks done.
    // Each task adds or subtracts a random amount from one of the
    // counters, and then schedules another task 0-1000
    // microseconds in the future to subtract or add from
    // the counter -random_amount+1, so in the end the shared
    // counters should sum to the number of initial tasks performed.
    CScheduler microTasks;

    boost::mutex counterMutex[10];
    int counter[10] = { 0 };
    FastRandomContext rng{/* fDeterministic */ true};
    auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9]
    auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
    auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    std::chrono::system_clock::time_point now = start;
    std::chrono::system_clock::time_point first, last;
    size_t nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 0);

    for (int i = 0; i < 100; ++i) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }
    nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 100);
    BOOST_CHECK(first < last);
    BOOST_CHECK(last > now);

    // As soon as these are created they will start running and servicing the queue
    boost::thread_group microThreads;
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));

    UninterruptibleSleep(std::chrono::microseconds{600});
    now = std::chrono::system_clock::now();

    // More threads and more tasks:
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
    for (int i = 0; i < 100; i++) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }

    // Drain the task queue then exit threads
    microTasks.stop(true);
    microThreads.join_all(); // ... wait until all the threads are done

    int counterSum = 0;
    for (int i = 0; i < 10; i++) {
        BOOST_CHECK(counter[i] != 0);
        counterSum += counter[i];
    }
    BOOST_CHECK_EQUAL(counterSum, 200);
}

BOOST_AUTO_TEST_CASE(wait_until_past)
{
    std::condition_variable condvar;
    Mutex mtx;
    WAIT_LOCK(mtx, lock);

    const auto no_wait= [&](const std::chrono::seconds& d) {
        return condvar.wait_until(lock, std::chrono::system_clock::now() - d);
    };

    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::minutes{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{10}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{100}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1000}));
}

BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
{
    CScheduler scheduler;

    // each queue should be well ordered with respect to itself but not other queues
    SingleThreadedSchedulerClient queue1(&scheduler);
    SingleThreadedSchedulerClient queue2(&scheduler);

    // create more threads than queues
    // if the queues only permit execution of one task at once then
    // the extra threads should effectively be doing nothing
    // if they don't we'll get out of order behaviour
    boost::thread_group threads;
    for (int i = 0; i < 5; ++i) {
        threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
    }

    // these are not atomic, if SinglethreadedSchedulerClient prevents
    // parallel execution at the queue level no synchronization should be required here
    int counter1 = 0;
    int counter2 = 0;

    // just simply count up on each queue - if execution is properly ordered then
    // the callbacks should run in exactly the order in which they were enqueued
    for (int i = 0; i < 100; ++i) {
        queue1.AddToProcessQueue([i, &counter1]() {
            bool expectation = i == counter1++;
            assert(expectation);
        });

        queue2.AddToProcessQueue([i, &counter2]() {
            bool expectation = i == counter2++;
            assert(expectation);
        });
    }

    // finish up
    scheduler.stop(true);
    threads.join_all();

    BOOST_CHECK_EQUAL(counter1, 100);
    BOOST_CHECK_EQUAL(counter2, 100);
}

BOOST_AUTO_TEST_CASE(mockforward)
{
    CScheduler scheduler;

    int counter{0};
    CScheduler::Function dummy = [&counter]{counter++;};

    // schedule jobs for 2, 5 & 8 minutes into the future
    int64_t min_in_milli = 60*1000;
    scheduler.scheduleFromNow(dummy, 2*min_in_milli);
    scheduler.scheduleFromNow(dummy, 5*min_in_milli);
    scheduler.scheduleFromNow(dummy, 8*min_in_milli);

    // check taskQueue
    std::chrono::system_clock::time_point first, last;
    size_t num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 3ul);

    std::thread scheduler_thread([&]() { scheduler.serviceQueue(); });

    // bump the scheduler forward 5 minutes
    scheduler.MockForward(std::chrono::seconds(5*60));

    // ensure scheduler has chance to process all tasks queued for before 1 ms from now.
    scheduler.scheduleFromNow([&scheduler]{ scheduler.stop(false); }, 1);
    scheduler_thread.join();

    // check that the queue only has one job remaining
    num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 1ul);

    // check that the dummy function actually ran
    BOOST_CHECK_EQUAL(counter, 2);

    // check that the time of the remaining job has been updated
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
    // should be between 2 & 3 minutes from now
    BOOST_CHECK(delta > 2*60 && delta < 3*60);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) <2017> <Thomas Ballenthin>
 *
 * 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.
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE linux auth test

#include <boost/test/unit_test.hpp>
#include <crypt.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string>

#include "authenticate.h"
#include "json/cJSON.h"

extern "C" {
	int cjet_timer_init(struct cjet_timer *timer, struct eventloop *loop){
		(void) timer;
		(void) loop;
		return 0;
	}

	void cjet_timer_destroy(struct cjet_timer *timer){
		(void) timer;
	}

	void cjet_get_random_bytes(void *bytes, size_t num_bytes)
	{
		BOOST_REQUIRE_MESSAGE(false,"the cjet_get_random_bytes function needs to be implemented, when it is actually used.");
		(void) bytes;
		(void) num_bytes;
	}
}

static std::string create_temp_copy_of_file(std::string source_filename, std::string filename_apendix)
{
	std::string destination_filename(source_filename);
	destination_filename.append(filename_apendix);

	std::ifstream source(source_filename.c_str(), std::ios::binary);
	std::ofstream dest(destination_filename.c_str(), std::ios::binary);

	BOOST_REQUIRE_MESSAGE(source != NULL, "Can't open source file: " << source_filename);
	BOOST_REQUIRE_MESSAGE(dest != NULL, "Can't open destination file: " << destination_filename);
	dest << source.rdbuf();

	source.close();
	dest.close();

	return destination_filename;
}

struct F {
	F()
	{
		int response = load_passwd_data("input_data/passwd_std.json");
		BOOST_REQUIRE_MESSAGE(response == 0, "Loading password file failed.");
	}

	~F()
	{
		free_passwd_data();
	}
};

BOOST_AUTO_TEST_CASE(check_load_passwd_data_error_paths)
{
	int response = load_passwd_data(NULL);
	BOOST_CHECK_MESSAGE(response == 0, "Expected 0 as return value when calling load_passwd_data(NULL).");

	response = load_passwd_data("*/___/");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when calling realpath with '*/___/'.");

	response = load_passwd_data("some_non_existing_file_641587976.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening non-existing file.");

	response = load_passwd_data("input_data/no_json.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening non JSON file.");

	response = load_passwd_data("input_data/passwd_no_user_data.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without user data.");

	response = load_passwd_data("input_data/passwd_fetch_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as fetch group.");

	response = load_passwd_data("input_data/passwd_set_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as set group.");

	response = load_passwd_data("input_data/passwd_call_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as callgroup.");

	response = load_passwd_data("input_data/passwd_no_json_data.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without any JSON.");
}

BOOST_FIXTURE_TEST_CASE(check_credentials, F)
{
	char username[] = "john";
	char passwd[] = "doe";

	char unknown_user[] = "mister_x";
	char passwd_to_null[] = "doe";

	char user_no_passwd[] = "john-no_passwd";
	char user_passwd_no_string[] = "john-pw_no_string";

	const cJSON *response1 = credentials_ok(NULL, NULL);
	BOOST_CHECK_MESSAGE(response1 == NULL, "Response should be NULL when no user_name nor passwd is provided.");

	const cJSON *response2 = credentials_ok(username, NULL);
	BOOST_CHECK_MESSAGE(response2 == NULL, "Response should be NULL when no passwd is provided.");

	const cJSON *response3 = credentials_ok(NULL, passwd_to_null);
	BOOST_CHECK_MESSAGE(response3 == NULL, "Response should be NULL when no user_name is provided.");
	BOOST_CHECK_MESSAGE(std::strcmp(passwd_to_null, "\0\0\0"), "The password has not been zeroed. Instead it was:\"" << passwd << "\".");

	const cJSON *response4 = credentials_ok(user_no_passwd, passwd_to_null);
	BOOST_CHECK_MESSAGE(response4 == NULL, "Response should be NULL when no password is given in the user database.");

	const cJSON *response5 = credentials_ok(user_passwd_no_string, passwd_to_null);
	BOOST_CHECK_MESSAGE(response5 == NULL, "Response should be NULL when password in database is not a string.");

	const cJSON *response6 = credentials_ok(unknown_user, passwd_to_null);
	BOOST_CHECK_MESSAGE(response6 == NULL, "Response should be NULL when password in database is not a string.");

	const cJSON *response7 = credentials_ok(username, passwd);
	BOOST_REQUIRE_MESSAGE(response7 != NULL, "User authentication failed even with correct credentials.");
}

BOOST_AUTO_TEST_CASE(check_credentials_no_user_data_loaded)
{
	char username[] = "john";
	char passwd[] = "doe";

	const cJSON *response1 = credentials_ok(username, passwd);
	BOOST_CHECK_MESSAGE(response1 == NULL, "Response should be NULL when no user data is loaded in before.");
}

BOOST_AUTO_TEST_CASE(test_copying)
{
	std::string temporarily_file = create_temp_copy_of_file("input_data/passwd_std.json", "_temp");
}
<commit_msg>auth_file_test: added plattform independet pseudo random number generation to test<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) <2017> <Thomas Ballenthin>
 *
 * 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.
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE linux auth test

#include <boost/test/unit_test.hpp>
#include <crypt.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>

#include "authenticate.h"
#include "peer.h"
#include "json/cJSON.h"

extern "C" {
int cjet_timer_init(struct cjet_timer *timer, struct eventloop *loop)
{
	(void)timer;
	(void)loop;
	return 0;
}

void cjet_timer_destroy(struct cjet_timer *timer)
{
	(void)timer;
}

void cjet_get_random_bytes(void *bytes, size_t num_bytes)
{
	uint8_t *buffer = (uint8_t *)bytes;
	srand(time(NULL));
	int random_number;

	for (size_t i = 0; i < num_bytes; i++) {
		random_number = rand();
		*buffer = random_number & 0xff;
		buffer++;
	}
}
}

static std::string create_temp_copy_of_file(std::string source_filename, std::string filename_apendix)
{
	std::string destination_filename(source_filename);
	destination_filename.append(filename_apendix);

	std::ifstream source(source_filename.c_str(), std::ios::binary);
	std::ofstream dest(destination_filename.c_str(), std::ios::binary);

	BOOST_REQUIRE_MESSAGE(source != NULL, "Can't open source file: " << source_filename);
	BOOST_REQUIRE_MESSAGE(dest != NULL, "Can't open destination file: " << destination_filename);
	dest << source.rdbuf();

	source.close();
	dest.close();

	return destination_filename;
}

static struct eventloop loop;

struct peer *alloc_peer()
{
	struct peer *p = (struct peer *)::malloc(sizeof(*p));
	init_peer(p, false, &loop);
	p->send_message = NULL;
	return p;
}

void free_peer(struct peer *p) { ::free(p); }

struct F {
	F()
	{
		std::string temporarily_file = create_temp_copy_of_file(
		    "input_data/passwd_std.json", "_temp");
		int response =
		    load_passwd_data("input_data/passwd_std.json_temp");
		BOOST_REQUIRE_MESSAGE(response == 0, "Loading password file failed.");
	}

	~F()
	{
		free_passwd_data();
	}
};

BOOST_AUTO_TEST_CASE(check_load_passwd_data_error_paths)
{
	int response = load_passwd_data(NULL);
	BOOST_CHECK_MESSAGE(response == 0, "Expected 0 as return value when calling load_passwd_data(NULL).");

	response = load_passwd_data("*/___/");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when calling realpath with '*/___/'.");

	response = load_passwd_data("some_non_existing_file_641587976.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening non-existing file.");

	response = load_passwd_data("input_data/no_json.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening non JSON file.");

	response = load_passwd_data("input_data/passwd_no_user_data.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without user data.");

	response = load_passwd_data("input_data/passwd_fetch_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as fetch group.");

	response = load_passwd_data("input_data/passwd_set_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as set group.");

	response = load_passwd_data("input_data/passwd_call_group_no_array.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without array as callgroup.");

	response = load_passwd_data("input_data/passwd_no_json_data.json");
	BOOST_CHECK_MESSAGE(response == -1, "Error expected when opening passwd file without any JSON.");
}

BOOST_FIXTURE_TEST_CASE(check_credentials, F)
{
	char username[] = "john";
	char passwd[] = "doe";

	char unknown_user[] = "mister_x";
	char passwd_to_null[] = "doe";

	char user_no_passwd[] = "john-no_passwd";
	char user_passwd_no_string[] = "john-pw_no_string";

	const cJSON *response1 = credentials_ok(NULL, NULL);
	BOOST_CHECK_MESSAGE(response1 == NULL, "Response should be NULL when no user_name nor passwd is provided.");

	const cJSON *response2 = credentials_ok(username, NULL);
	BOOST_CHECK_MESSAGE(response2 == NULL, "Response should be NULL when no passwd is provided.");

	const cJSON *response3 = credentials_ok(NULL, passwd_to_null);
	BOOST_CHECK_MESSAGE(response3 == NULL, "Response should be NULL when no user_name is provided.");
	BOOST_CHECK_MESSAGE(std::strcmp(passwd_to_null, "\0\0\0"), "The password has not been zeroed. Instead it was:\"" << passwd << "\".");

	const cJSON *response4 = credentials_ok(user_no_passwd, passwd_to_null);
	BOOST_CHECK_MESSAGE(response4 == NULL, "Response should be NULL when no password is given in the user database.");

	const cJSON *response5 = credentials_ok(user_passwd_no_string, passwd_to_null);
	BOOST_CHECK_MESSAGE(response5 == NULL, "Response should be NULL when password in database is not a string.");

	const cJSON *response6 = credentials_ok(unknown_user, passwd_to_null);
	BOOST_CHECK_MESSAGE(response6 == NULL, "Response should be NULL when password in database is not a string.");

	const cJSON *response7 = credentials_ok(username, passwd);
	BOOST_REQUIRE_MESSAGE(response7 != NULL, "User authentication failed even with correct credentials.");
}

BOOST_AUTO_TEST_CASE(check_credentials_no_user_data_loaded)
{
	char username[] = "john";
	char passwd[] = "doe";

	const cJSON *response1 = credentials_ok(username, passwd);
	BOOST_CHECK_MESSAGE(response1 == NULL, "Response should be NULL when no user data is loaded in before.");
}

BOOST_FIXTURE_TEST_CASE(change_credentials, F)
{
	char username[] = "john";
	char old_passwd[] = "doe";
	char new_passwd[] = "secret";

	cJSON *fake_request = cJSON_CreateObject();
	peer *test_peer = alloc_peer();
	test_peer->user_name = username;

	cJSON *response = change_password(test_peer, fake_request, username, new_passwd);

	cJSON_Delete(fake_request);
	free_peer(test_peer);
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include "tests/script.hpp"

// Run each of the sample frameworks in local mode.
TEST_SCRIPT(ExamplesTest, TestFramework, "test_framework_test.sh")
TEST_SCRIPT(ExamplesTest, NoExecutorFramework, "no_executor_framework_test.sh")

TEST_SCRIPT(ExamplesTest, EventCallFramework, "event_call_framework_test.sh")

TEST_SCRIPT(ExamplesTest, PersistentVolumeFramework,
            "persistent_volume_framework_test.sh")

#ifdef MESOS_HAS_JAVA
TEST_SCRIPT(ExamplesTest, JavaFramework, "java_framework_test.sh")
TEST_SCRIPT(ExamplesTest, JavaException, "java_exception_test.sh")
TEST_SCRIPT(ExamplesTest, JavaLog, "java_log_test.sh")
#endif

#ifdef MESOS_HAS_PYTHON
TEST_SCRIPT(ExamplesTest, PythonFramework, "python_framework_test.sh")
#endif
<commit_msg>Temporarily disabled EventCallFramework test due to MESOS-3273.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include "tests/script.hpp"

// Run each of the sample frameworks in local mode.
TEST_SCRIPT(ExamplesTest, TestFramework, "test_framework_test.sh")
TEST_SCRIPT(ExamplesTest, NoExecutorFramework, "no_executor_framework_test.sh")

// Temporarily disabled this test due to MESOS-3273.
TEST_SCRIPT(DISABLED_ExamplesTest, EventCallFramework,
            "event_call_framework_test.sh")

TEST_SCRIPT(ExamplesTest, PersistentVolumeFramework,
            "persistent_volume_framework_test.sh")

#ifdef MESOS_HAS_JAVA
TEST_SCRIPT(ExamplesTest, JavaFramework, "java_framework_test.sh")
TEST_SCRIPT(ExamplesTest, JavaException, "java_exception_test.sh")
TEST_SCRIPT(ExamplesTest, JavaLog, "java_log_test.sh")
#endif

#ifdef MESOS_HAS_PYTHON
TEST_SCRIPT(ExamplesTest, PythonFramework, "python_framework_test.sh")
#endif
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//mapnik
#include <mapnik/text/placements/list.hpp>
#include <mapnik/xml_node.hpp>
#include <mapnik/make_unique.hpp>
//boost
#include <boost/property_tree/ptree.hpp>

namespace mapnik
{

bool text_placement_info_list::next()
{
    if (state == 0)
    {
        properties = parent_->defaults;
    }
    else
    {
        if (state  > parent_->list_.size()) return false;
        properties = parent_->list_[state-1];
    }
    ++state;
    return true;
}

text_symbolizer_properties & text_placements_list::add()
{
    if (list_.size())
    {
        text_symbolizer_properties & last = list_.back();
        list_.push_back(last); //Preinitialize with old values FIXME
    }
    else
    {
        list_.push_back(defaults);
    }
    return list_.back();
}

text_symbolizer_properties & text_placements_list::get(unsigned i)
{
    return list_[i];
}

text_placement_info_ptr text_placements_list::get_placement_info(double scale_factor) const
{
    return std::make_unique<text_placement_info_list>(this, scale_factor);
}

text_placements_list::text_placements_list()
    : text_placements(),
      list_(0) {}

void text_placements_list::add_expressions(expression_set & output) const
{
    defaults.add_expressions(output);
    for (auto & prop : list_)
    {
        prop.add_expressions(output);
    }
}

unsigned text_placements_list::size() const
{
    return list_.size();
}

text_placements_ptr text_placements_list::from_xml(xml_node const& xml, fontset_map const& fontsets)
{
    using boost::property_tree::ptree;
    text_placements_list *list = new text_placements_list;
    text_placements_ptr ptr = text_placements_ptr(list);
    list->defaults.from_xml(xml, fontsets);
    xml_node::const_iterator itr = xml.begin();
    xml_node::const_iterator end = xml.end();
    for( ;itr != end; ++itr)
    {
        if (itr->is_text() || !itr->is("Placement")) continue;
        text_symbolizer_properties & p = list->add();
        //p.format = std::make_shared<detail::evaluated_format_properties>(*(p.format)); //Make a deep copy -- FIXME
        //TODO: This needs a real copy constructor for text_symbolizer_properties
        p.from_xml(*itr, fontsets);
       //TODO: if (strict_ && !p.format.fontset.size())
       //         ensure_font_face(p.format.face_name);
    }
    return ptr;
}

} //ns mapnik
<commit_msg>c++11 : cleanup crufty code<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//mapnik
#include <mapnik/text/placements/list.hpp>
#include <mapnik/xml_node.hpp>
#include <mapnik/make_unique.hpp>
//boost
#include <boost/property_tree/ptree.hpp>

namespace mapnik
{

bool text_placement_info_list::next()
{
    if (state == 0)
    {
        properties = parent_->defaults;
    }
    else
    {
        if (state  > parent_->list_.size()) return false;
        properties = parent_->list_[state-1];
    }
    ++state;
    return true;
}

text_symbolizer_properties & text_placements_list::add()
{
    if (list_.size())
    {
        text_symbolizer_properties & last = list_.back();
        list_.push_back(last); //Preinitialize with old values FIXME
    }
    else
    {
        list_.push_back(defaults);
    }
    return list_.back();
}

text_symbolizer_properties & text_placements_list::get(unsigned i)
{
    return list_[i];
}

text_placement_info_ptr text_placements_list::get_placement_info(double scale_factor) const
{
    return std::make_unique<text_placement_info_list>(this, scale_factor);
}

text_placements_list::text_placements_list()
    : text_placements(),
      list_(0) {}

void text_placements_list::add_expressions(expression_set & output) const
{
    defaults.add_expressions(output);
    for (auto & prop : list_)
    {
        prop.add_expressions(output);
    }
}

unsigned text_placements_list::size() const
{
    return list_.size();
}

text_placements_ptr text_placements_list::from_xml(xml_node const& node, fontset_map const& fontsets)
{
    auto list = std::make_shared<text_placements_list>();
    list->defaults.from_xml(node, fontsets);
    for( auto const& child : node)
    {
        if (child.is_text() || !child.is("Placement")) continue;
        text_symbolizer_properties & p = list->add();
        p.from_xml(child, fontsets);
        //if (strict_ && !p.format.fontset.size())
        //    ensure_font_face(p.format.face_name);
    }
    return list;
}

} //ns mapnik
<|endoftext|>