/* -*- C++ -*- */
// $Id: QtReactor_Test.cpp 91626 2010-09-07 10:59:20Z johnnyw $

// ============================================================================
//
// = LIBRARY
//    tests
//
// = FILENAME
//    QtReactor_Test.cpp
//
// = DESCRIPTION
//    Simple test of QtReactor. Test is intended to verify if QtReactor
//    correctly cooperates with Qt event loop in typical application. Test
//    creates a number of timers which send datagrams. These datagrams are
//    expected by datagram_handlers registered in reactor. Test asynchronously
//    establishes also a number of loopback tcp connections using ACE_Acceptors
//    and ACE_Connectors. Socket activities are handled asynchronously to ensure
//    that reactor does not lose events in traffic transmission. Moreover, test
//    registers and removes handlers frequently to cover register/remove_handler
//    method of QtReactor which are known (10/07/2004) to be buggy.
//
//    Classes:
//      QTestApplication     - main qt application running event loop for a
//                             finite time
//      Dgram_Handler        - responsible for sending and receiving datagrams as
//                             well as handling timeouts. Datagrams are sent in
//                             handle_timeout method.
//      TCPConnectionHandler - connection handler responsible for sending and
//                             receiving data using tcp streams.
//      TCPAcceptorHandler   - acceptor responsible for acceptance and
//                             registration of connections in HandlersRegister
//                             class.
//      HandlersRegister     - register of event_handlers, responsible also for
//                             the analysis of test results.
//
// = AUTHOR
//    Marek Brudka <mbrudka@elka.pw.edu.pl>
//
// ============================================================================

#include "test_config.h"

#include <assert.h>
#include <qapplication.h>
#include <qtimer.h>

#include "ace/OS_NS_time.h"
#include "ace/Time_Value.h"
#include "ace/QtReactor/QtReactor.h"
#include "ace/Event_Handler.h"
#include "ace/Acceptor.h"
#include "ace/Connector.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/SOCK_Connector.h"
#include "ace/SOCK_Dgram.h"

// Qt specific code
#include "QtReactor_Test.h"

/*
   QTestApplication class implementation
*/

QTestApplication::QTestApplication (int argc, char *argv[]):
  QApplication (argc, argv, FALSE) /* do not enable GUI */
{
  //FUZZ: disable check_for_lack_ACE_OS
  connect (&finishTimer_, SIGNAL (timeout ()), this, SLOT (finishTest ()));
  //FUZZ: enable check_for_lack_ACE_OS
}

void QTestApplication::finishTest ()
{
  ACE_OS::exit ();
}

void QTestApplication::exec (int msec)
{
  finishTimer_.stop ();
  if (0 < msec )
    finishTimer_.start (msec, TRUE);
  inherited::exec ();
}


// maximum time for testing QtReactor (msec)
const int TotalTestTime = 8000;

// how many handlers for each event handler class should be started ?
#ifndef __QNXNTO__
const int HandlersNo = 8 ;
#else /* __QNXNTO__ */
// it seems that Qt 3.1 for NTO 6.2 is compiled with rather small FD_SETSIZE
// Nevertheless, test works fine with native select reactor
const int HandlersNo = 4;
#endif
// base port for sending datagrams
const u_short BaseDgramPort = 5931;

// port for TCP connections
const u_short BaseTCPPort = 5931;

// how many datagrams should be sent for a single handler
const int DgramsToSend = 64;

// how many bytes to send vie TCP
const int TCPBytesToSend = 1024;

// how many times to reply tcp recv
const int TCPClientPings = 16;

// total number of bytes in TCP transmission
const int TCPTotalBytesToSend = TCPClientPings * TCPBytesToSend;

/**
  \class DgramHandler
  \brief Simple event handler that receives and counts datagrams
  as well as sends dgrams using timeouts.
*/
class DgramHandler: public ACE_Event_Handler
{
public:
  DgramHandler (ACE_Reactor *p_reactor = 0);
  virtual ~DgramHandler ();

  //FUZZ: disable check_for_lack_ACE_OS
  int  open (const ACE_INET_Addr &local,
             int protocol_family=ACE_PROTOCOL_FAMILY_INET,
             int protocol=0,
             int reuse_addr=0);
  //FUZZ: enable check_for_lack_ACE_OS

  virtual ACE_HANDLE get_handle () const;
  virtual int handle_input (ACE_HANDLE handle);
  virtual int handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask);
  virtual int handle_timeout (const ACE_Time_Value &current_time, const void *act=0);
  int         dgramsSent () const;
  int         dgramsReceived () const;
  int         timeoutsTriggered () const;
  int         expectedTriggers () const;
  void        expectedTriggers (int);
private:
  int dgramsSent_;      //!< the number of sent datagrams
  int dgramsReceived_;  //!< the number of received datagrams
  int timeoutsTriggered_;
  int expectedTriggers_;
  ACE_SOCK_Dgram peer_; //!< datagram socket we listen to
};

/**
  \class TCPConnectionHandler
  \brief TCP stream handler for both sides of connection.
*/
class TCPConnectionHandler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
{
public:
  typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited;
public:
  TCPConnectionHandler (bool p_serverSide = false);
  virtual ~TCPConnectionHandler ();

  virtual int handle_output (ACE_HANDLE handle);
  virtual int handle_input (ACE_HANDLE handle);
  virtual int handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask);

  //FUZZ: disable check_for_lack_ACE_OS
  virtual int open (void * = 0);
  //FUZZ: enable check_for_lack_ACE_OS

  int scheduleSend (ACE_Message_Block *);
  int sendBuffers ();
  int totalReceived () const;
  int totalSent () const;
private:
  ACE_Message_Block *buffers_;
  int totalReceived_; //!< total number of received bytes
  int totalSent_; //!< total number of send bytes
  bool serverSide_; //!< if true, echo received data
  int pingsNo_; //!< number of pings client should make
};

typedef ACE_Connector< TCPConnectionHandler, ACE_SOCK_CONNECTOR > TCPConnectorHandler;
class TCPAcceptorHandler; // forward declaration

/* \class HandlersRegister
   \brief The collection of test tools. Here ACE classes bind to Qt application
*/
class HandlersRegister
{
public:
  HandlersRegister (ACE_Reactor *p_reactor);
  virtual ~HandlersRegister ();
  int scheduleTimers (const ACE_Time_Value &p_TestTime); //!< schedule ace timers
  int registerDgramHandlers (); //!< open dgrams socket and register in reactor
  int registerTCPHandlers ();  //!< creates TCP acceptors and connector
  int analyze () const; //!< analyze results
  int analyzeTimeouts () const; //!< analyze triggered timeouts
  int analyzeDgrams () const; //!< analyze collected dgrams
  int analyzeTCP () const; //!< analyze TCP transmission
  int registerTCPServer (TCPConnectionHandler *);
  int TCPServersNo () const; //!< return the number of accepted connections

private:
  ACE_Reactor          *reactor_; //!< reactor for this application
  DgramHandler         *DgramHandlers_[ HandlersNo] ;//!< dgram input handlers
  int                  TCPServersNo_; //!< number of accepted connections
  TCPConnectionHandler *TCPServers_[ HandlersNo ];
  TCPConnectionHandler *TCPClients_[ HandlersNo ];
  TCPAcceptorHandler   *acceptor_;
  TCPConnectorHandler  *connectors_[ HandlersNo ];
};

class TCPAcceptorHandler : public ACE_Acceptor< TCPConnectionHandler, ACE_SOCK_ACCEPTOR >
{
public:
  typedef ACE_Acceptor< TCPConnectionHandler, ACE_SOCK_ACCEPTOR > inherited;
public:
  TCPAcceptorHandler (HandlersRegister *p_handlersRegister);
  virtual ~TCPAcceptorHandler ();
  virtual int make_svc_handler (TCPConnectionHandler *& sh);
  virtual int activate_svc_handler (TCPConnectionHandler * sh);
private:
  HandlersRegister *handlersRegister_;
};

/*
   DgramHandler class implementation
*/

DgramHandler::DgramHandler (ACE_Reactor *p_reactor):
  ACE_Event_Handler (p_reactor),
  dgramsSent_ (0),
  dgramsReceived_ (0),
  timeoutsTriggered_ (0),
  expectedTriggers_ (0)
{
  reference_counting_policy ().value (Reference_Counting_Policy::ENABLED);
}

DgramHandler::~DgramHandler ()
{
  ACE_TRACE ("DgramHandler::~DgramHandler");
}

int  DgramHandler::open (const ACE_INET_Addr &local,
  int protocol_family,
  int protocol,
  int reuse_addr)
{
  if (0 > peer_.open (local,
                      protocol_family,
                      protocol,
                      reuse_addr))
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P) %p\n"),
                       ACE_TEXT ("Cannot oper dgram socket")),
                      -1);

  return 0;
}

ACE_HANDLE DgramHandler::get_handle () const
{
  return peer_.get_handle ();
}

int DgramHandler::handle_input (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);
  int recvBuffer;
  ACE_INET_Addr peerAddress;
  int result;

  result = peer_.recv (&recvBuffer, sizeof (recvBuffer) , peerAddress);

  if (0 >= result)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P) %p\n"),
                       ACE_TEXT ("While reading datagram from socket"))
                      , -1);
  else
    ++dgramsReceived_;

  return 0;
}

int DgramHandler::handle_timeout (const ACE_Time_Value &current_time, const void *act)
{
  ACE_UNUSED_ARG (current_time);
  ACE_UNUSED_ARG (act);
  int sendBuffer = 0;

  if (++timeoutsTriggered_ >= expectedTriggers_)
    reactor ()->cancel_timer (this, 1);

  ACE_SOCK_Dgram socket;
  if (-1 == socket.open (ACE_INET_Addr (static_cast< u_short > (0),
                                        static_cast< ACE_UINT32 > (INADDR_ANY)),
                                        ACE_PROTOCOL_FAMILY_INET, 0, 1))
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT (" (%P) %p\n"),
                ACE_TEXT ("Cannot open socket for sending Qt dgrams")));

  ACE_INET_Addr peerAddr;
  peer_.get_local_addr (peerAddr);

  if (sizeof (sendBuffer) != socket.send (&sendBuffer,
                                          sizeof (sendBuffer), peerAddr))
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT (" (%P) %p\n"),
                ACE_TEXT ("Cannot send dgram")));
  else
    ++dgramsSent_;

  socket.close ();

  return 0;
}

int DgramHandler::handle_close (ACE_HANDLE handle, ACE_Reactor_Mask close_mask)
{
  if (peer_.get_handle () != handle )
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Unknown handle %d DgramHandler::handle_close "
                          "with mask %x. My handle is %d\n"),
                handle,
                close_mask,
                peer_.get_handle ()));
  else
    peer_.close ();

  return 0;
}

int DgramHandler::dgramsSent () const
{
  return dgramsSent_;
}

int DgramHandler::dgramsReceived () const
{
  return dgramsReceived_;
}

int DgramHandler::timeoutsTriggered () const
{
  return timeoutsTriggered_;
}

void DgramHandler::expectedTriggers (int triggers)
{
  expectedTriggers_ = triggers;
}

int DgramHandler::expectedTriggers () const
{
  return expectedTriggers_;
}

/*
   TCPConnectionHandler class implementation
*/

TCPConnectionHandler::TCPConnectionHandler (bool p_serverSide):
  buffers_ (0),
  totalReceived_ (0),
  totalSent_ (0),
  serverSide_ (p_serverSide),
  pingsNo_ (TCPClientPings)
{
  reference_counting_policy ().value (Reference_Counting_Policy::ENABLED);
}

TCPConnectionHandler::~TCPConnectionHandler ()
{
  ACE_TRACE ("TCPConnectionHandler::~TCPConnectionHandler");
}

int TCPConnectionHandler::handle_input (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);
  ACE_Message_Block *buffer = new ACE_Message_Block (TCPBytesToSend);
  int bytesReceived = peer_.recv (buffer->wr_ptr (), buffer->space ());

  if (bytesReceived > 0)
    {
      totalReceived_ += bytesReceived;
      if (serverSide_ || --pingsNo_ > 0) // echo received buffer
        {
          buffer->wr_ptr (bytesReceived);
          int result = scheduleSend (buffer);
          if (0 > result)
            ACE_ERROR_RETURN ((LM_ERROR,
                               ACE_TEXT (" (%P) %p\n"),
                               ACE_TEXT ("Cannot schedule TCP reply")),
                              -1);
        }
      else
        buffer->release ();

      return 0;
    }

  if (errno != EWOULDBLOCK)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P:%p (%d)\n"),
                       ACE_TEXT ("TCPConnectionHandler::handle_input call with no data on handle "), handle),
                      -1);

  ACE_ERROR ((LM_WARNING,
              ACE_TEXT (" (%P:%p (%d)\n"),
              ACE_TEXT ("TCPConnectionHandler::handle_input call with no data on handle "), handle));

  return 0;
}

int TCPConnectionHandler::handle_output (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);
  if (!buffers_)
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("TCPConnectionHandler::handle_output call for empty buffers (%d)\n"), handle));
  if (0 > sendBuffers ()) // socket broken, kill yourself
    return -1;

  if (!buffers_) // everything already send, unregister
    {
      reactor ()->cancel_wakeup (this,
                                 ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::DONT_CALL);
      reactor ()->remove_handler (this,
                                  ACE_Event_Handler::WRITE_MASK  | ACE_Event_Handler::DONT_CALL);
    }

  return 0;
}

int TCPConnectionHandler::open (void * )
{
  int result = inherited::open ();

  if (!serverSide_)
    {
      ACE_Message_Block *buffer = new ACE_Message_Block (TCPBytesToSend);
      char *bufferData = buffer->wr_ptr ();
      int i;

      for (i = buffer->size () - 1; i >= 0; --i)
        bufferData[ i ] = static_cast< char > (i);
      buffer->wr_ptr (buffer->size ());
      if (0 != (scheduleSend (buffer)))
        ACE_ERROR_RETURN ((LM_ERROR,
                           ACE_TEXT ("Cannot schedule initial send\n")),
                          -1);
    }

  return result;
}

int TCPConnectionHandler::scheduleSend (ACE_Message_Block * buffer)
{
  // link buffer to the end of buffers list
  if (buffers_)
    {
      ACE_Message_Block *lastBuffer = buffers_;
      while (lastBuffer->cont ())
        lastBuffer = lastBuffer->cont () ;
      lastBuffer->cont (buffer);
    }
  else
    buffers_ = buffer;

  if (0 > sendBuffers ())
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P) %p\n"),
                       ACE_TEXT ("Cannot schedule TCP send.")),
                      -1);
  return 0;
}

int TCPConnectionHandler::sendBuffers ()
{
  int result = 0;

  if (buffers_)
    if (0 < (result = peer_.send_n (buffers_))) // remove sent blocks
      {
        totalSent_ += result;
        while (buffers_ &&
               static_cast< size_t > (result) >= buffers_->length ())
          {
            ACE_Message_Block *buffer = buffers_;
            result -= buffers_->length ();
            buffers_= buffers_->cont ();
            buffer->cont (0);
            buffer->release ();
          }

        if (buffers_) // some buffers were not sent, truncate data
          buffers_->rd_ptr (result);
      }

  return result;
}

int TCPConnectionHandler::handle_close (ACE_HANDLE handle,ACE_Reactor_Mask close_mask)
{
  if (peer_.get_handle () != handle )
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Unknown handle %d TCPConnectionHandler::handle_close "
                          "with mask %x. My handle is %d\n"),
                handle,
                close_mask,
                peer_.get_handle ()));
  else
    peer_.close ();

  return 0;
}

int TCPConnectionHandler::totalReceived () const
{
  return totalReceived_;
}

int TCPConnectionHandler::totalSent () const
{
  return totalSent_;
}


/*
   HandlersRegister class implementation
*/

HandlersRegister::HandlersRegister (ACE_Reactor *p_reactor):
  reactor_ (p_reactor),
  TCPServersNo_ (0)
{
  int i;

  for (i = 0; i < HandlersNo; ++i)
    {
      // create dgram input handler
      DgramHandlers_[ i ] = new DgramHandler (p_reactor);

      TCPServers_[ i ] = 0;

      TCPClients_[ i ] = new TCPConnectionHandler (false);

      connectors_[ i ] =new TCPConnectorHandler (p_reactor, ACE_NONBLOCK);
      connectors_[ i ]->reference_counting_policy ().value (
                                                            ACE_Event_Handler::Reference_Counting_Policy::ENABLED) ;
    }

  acceptor_ = new TCPAcceptorHandler (this);
  acceptor_->reactor (p_reactor);
}

HandlersRegister::~HandlersRegister ()
{
  int i;
  if (acceptor_)
    {
      reactor_->remove_handler (acceptor_, ACE_Event_Handler::ALL_EVENTS_MASK);
      acceptor_->close ();
      acceptor_->remove_reference ();
    }

  for (i = 0; i < HandlersNo; ++i)
    {
      reactor_->cancel_timer (DgramHandlers_[ i ], 1);
      reactor_->remove_handler (DgramHandlers_[ i ],
                                ACE_Event_Handler::ALL_EVENTS_MASK);
      DgramHandlers_[ i ]->remove_reference ();

      if (TCPServers_[ i ])
        {
          reactor_->remove_handler (TCPServers_[ i ],
                                    ACE_Event_Handler::ALL_EVENTS_MASK);
          TCPServers_[ i ]->remove_reference ();
        }

      reactor_->remove_handler (connectors_[ i ],
                                ACE_Event_Handler::ALL_EVENTS_MASK);
      connectors_[ i ]->close ();
      connectors_[ i ]->remove_reference ();

      if (TCPClients_[ i ])
        {
          reactor_->remove_handler (TCPClients_[ i ],
                                    ACE_Event_Handler::ALL_EVENTS_MASK);
          TCPClients_[ i ]->remove_reference ();
        }
    }
}

int HandlersRegister::TCPServersNo () const
{
  return TCPServersNo_;
}

int HandlersRegister::scheduleTimers (const ACE_Time_Value &p_TestTime)
{
  int i;

  for (i = 0; i < HandlersNo; ++i)
    {
      if (-1 == reactor_->schedule_timer (DgramHandlers_[ i ],
                                          (const void *) 0,
                                          ACE_Time_Value::zero,
                                          p_TestTime * (0.5 / DgramsToSend)))
        ACE_ERROR_RETURN ((LM_ERROR,
                           ACE_TEXT (" (%P) %p\n"),
                           ACE_TEXT ("Cannot schedule ACE timer")),
                          -1);

      DgramHandlers_[ i ] ->expectedTriggers (DgramsToSend);

    }

  return 0;
}

int HandlersRegister::registerDgramHandlers ()
{
  int i;

  // open dgram handlers for all ports
  for (i = 0; i < HandlersNo; ++i)
    if (-1 == DgramHandlers_[ i ]->open (
                                         ACE_INET_Addr (i + BaseDgramPort,
                                                        ACE_TEXT ("127.0.0.1"),
                                                        ACE_PROTOCOL_FAMILY_INET)))
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT (" (%P) %p\n"),
                         ACE_TEXT ("Cannot open dgram handler")),
                        -1);

  // register dgram handlers
  for (i = 0; i < HandlersNo; ++i)
    if (-1 == reactor_->register_handler (DgramHandlers_[ i ],
                                          ACE_Event_Handler::READ_MASK))
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT (" (%P) %p\n"),
                         ACE_TEXT ("Cannot register dgram handler")),
                        -1);
  return 0;
}

int HandlersRegister::registerTCPHandlers ()
{
  ACE_INET_Addr addr (BaseTCPPort);

  if (-1 == acceptor_->open (addr, reactor_, 1))
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P) %p\n"),
                       ACE_TEXT ("Cannot open acceptor port")),
                      -1);

  int i;
  addr.set (BaseTCPPort, ACE_TEXT ("127.0.0.1"));

  for (i = 0; i < HandlersNo; ++i)
    {
      if (-1 == connectors_[ i ]->connect (
                                           TCPClients_[ i ],
                                           addr,
                                           ACE_Synch_Options::asynch))
        if (errno != EWOULDBLOCK )
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT (" (%P) %p (%d)\n"),
                             ACE_TEXT ("Cannot connect connector"),
                             i),
                            -1);
    }

  return 0;
}

int HandlersRegister::registerTCPServer (TCPConnectionHandler *handler)
{
  if (TCPServersNo_ < HandlersNo)
    {
      TCPServers_[ TCPServersNo_++ ] = handler;
      return 0;
    }
  ACE_ERROR ((LM_ERROR,
              ACE_TEXT ("Too many servers registered (%d). ACE_Reactor or ACE_Acceptor broken?\n"),
              handler->get_handle ()));
  return -1;
}

int HandlersRegister::analyze () const
{
  int result = 0;

  if (0 > analyzeTimeouts ())
    result = -1;

  if (0 > analyzeDgrams ())
    result = -1;

  if (0 > analyzeTCP ())
    result = -1;

  return result;
}

int HandlersRegister::analyzeTimeouts () const
{
  int i;
  int result = 0;

  for (i = 0; i < HandlersNo; ++i)
    if (DgramHandlers_[ i ]->expectedTriggers () !=
        DgramHandlers_[ i ]->timeoutsTriggered ())
      {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Dgram_Handlers (%d) expected %d timeouts"
                              " but triggered only %d.\n"),
                    i,
                    DgramHandlers_[ i ]->expectedTriggers (),
                    DgramHandlers_[ i ]->timeoutsTriggered ()));
        result = -1;
      }

  return result;
}

int HandlersRegister::analyzeDgrams () const
{
  int result = 0;
  int i;

  for (i = 0; i < HandlersNo; ++i)
    if (DgramHandlers_[ i ]->dgramsSent () !=
        DgramHandlers_[ i ]->dgramsReceived ())
      {
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("DgramsHandler (%d) sent  %d dgrams but received only %d."
                              "Either reactor failed or system lost local dgrams..\n"),
                    DgramHandlers_[ i ]->dgramsSent (),
                    DgramHandlers_[ i ]->dgramsReceived ()));
        result = -1 ;
      }

  return result;
}

int HandlersRegister::analyzeTCP () const
{
  int i;
  int result = 0;

  for (i = 0; i < HandlersNo; ++i)
    {
      if (TCPClients_[ i ]->totalSent () != TCPTotalBytesToSend )
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("TCPClient (%d): wanted to send  %d  but sent only (%d).\n"),
                      i,
                      TCPTotalBytesToSend,
                      TCPClients_[ i ]->totalSent ()));
          result = -1;
        }

      if (TCPClients_[ i ]->totalReceived () != TCPTotalBytesToSend )
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("TCPClient (%d): expected %d bytes but received only (%d).\n"),
                      i,
                      TCPTotalBytesToSend,
                      TCPClients_[ i ]->totalReceived ()));
          result = -1;
        }

      if (TCPServers_[ i ])
        {
          if (TCPServers_[ i ]->totalSent () != TCPTotalBytesToSend )
            {
              ACE_ERROR ((LM_ERROR,
                          ACE_TEXT ("TCPServer (%d): wanted to send %d bytes "
                                    "but sent only (%d).\n"),
                          i,
                          TCPTotalBytesToSend,
                          TCPServers_[ i ]->totalSent ()));
              result = -1;
            }

          if (TCPServers_[ i ]->totalReceived () != TCPTotalBytesToSend )
            {
              ACE_ERROR ((LM_ERROR,
                          ACE_TEXT ("TCPServer (%d): expected %d bytes but received only (%d).\n"),
                          i,
                          TCPTotalBytesToSend,
                          TCPServers_[ i ]->totalReceived ()));
              result = -1;
            }

        }
      else
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("TCPServer (%d): not connected.\n"),
                      i));
          result = -1;
        }
    }

  return result;
}


/*
   TCPAcceptorHandler class implementation
*/

TCPAcceptorHandler::TCPAcceptorHandler (HandlersRegister *p_handlersRegister):
  handlersRegister_ (p_handlersRegister)
{
  reference_counting_policy ().value (Reference_Counting_Policy::ENABLED);
}

int TCPAcceptorHandler::make_svc_handler (TCPConnectionHandler *& sh)
{
  sh = new TCPConnectionHandler (true);
  sh->reactor (reactor ());
  if (handlersRegister_->TCPServersNo () >= HandlersNo)
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("TCPAcceptorHandler::make_svc_handler called to many times!\n")));
  else
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("TCPAcceptorHandler::make_svc_handler new TCP server created\n")));

  return 0;
}

int TCPAcceptorHandler::activate_svc_handler (TCPConnectionHandler * sh)
{
  if (0 == inherited::activate_svc_handler (sh) )
    {
      if (0 != handlersRegister_->registerTCPServer (sh))  // for analysis
        {
          // release event handler
          reactor ()->remove_handler (sh, ACE_Event_Handler::ALL_EVENTS_MASK);
          sh->remove_reference ();
          // report error
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Cannot register server TCPConnectionHandler\n")),
                            -1);
        }

    }
  else
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("Failed to create server TCPConnectionHandler\n")),
                      -1);

  return 0;
}

TCPAcceptorHandler::~TCPAcceptorHandler ()
{
  ACE_TRACE ("TCPAcceptorHandler::~TCPAcceptorHandler");
}

void testNativeReactor (int argc, ACE_TCHAR *argv[])
{
  ACE_UNUSED_ARG (argc);
  ACE_UNUSED_ARG (argv);

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Testing autotest using native reactor\n")));

  ACE_Reactor      reactor;
  HandlersRegister handlersRegister (&reactor);
  ACE_Time_Value   testTime (TotalTestTime / 1000, (TotalTestTime % 1000) * 1000);

  if (0 <= handlersRegister.scheduleTimers (testTime)  &&
      0 <= handlersRegister.registerDgramHandlers () &&
      0 <= handlersRegister.registerTCPHandlers ())
    {
      reactor.run_reactor_event_loop (testTime);

      if (0  != handlersRegister.analyze ())
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("Test failed for native reactor. "
                              "Fix QtReactor_Test or ACE_Reactor.\n")));
      else
        ACE_ERROR ((LM_INFO, ACE_TEXT ("Test seems to work with native reactor.\n")));
    }
}

void testQtReactor (int argc, ACE_TCHAR *argv[])
{
  // Qt specific code
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Testing QtReactor\n")));

  QTestApplication app (argc, argv);
  ACE_QtReactor    qtReactor (&app);
  ACE_Reactor      reactor (&qtReactor);
  HandlersRegister handlersRegister (&reactor);
  ACE_Time_Value   testTime (TotalTestTime / 1000, (TotalTestTime % 1000) * 1000);

  if (0 <= handlersRegister.scheduleTimers (testTime)  &&
      0 <= handlersRegister.registerDgramHandlers () &&
      0 <= handlersRegister.registerTCPHandlers ())
    {

      app.exec (TotalTestTime);

      if (0  != handlersRegister.analyze ())
        ACE_ERROR ((LM_ERROR, ACE_TEXT ("QtReactor_Test failed.\n")));
      else
        ACE_ERROR ((LM_INFO, ACE_TEXT ("QtReactor_Test passed.\n")));
    }
}

int
run_main (int argc, ACE_TCHAR *argv[])
{
  ACE_START_TEST (ACE_TEXT ("QtReactor_Test"));

  testNativeReactor (argc, argv);
  testQtReactor (argc, argv);

  ACE_END_TEST;

  return 0;
}
