// -*- C++ -*-


// ============================================================================
/**
 *  @file test_config.h
 *
 *   This file factors out common macros and other utilities used by the
 *   ACE automated regression tests.
 *
 *  @author Prashant Jain <pjain@cs.wustl.edu>
 *  @author Tim Harrison <harrison@cs.wustl.edu>
 *  @author David Levine <levine@cs.wustl.edu>
 */
// ============================================================================

#ifndef ACE_TEST_CONFIG_H
#define ACE_TEST_CONFIG_H

#include "ace/config-all.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

#if defined (ACE_NLOGGING)
// ACE_NLOGGING must not be set if the tests are to produce any output.
#undef ACE_NLOGGING
#endif /* ACE_NLOGGING */

// This first #undef protects against command-line definitions.
#undef ACE_NDEBUG
#include "ace/streams.h"
#include "ace/Singleton.h"
#include "ace/Synch.h"
#include "ace/Log_Msg.h"
#include "ace/ACE.h"

// The second #undef protects against being reset in a config.h file.
#undef ACE_NDEBUG

#if defined (ACE_HAS_WINCE)
// Note that Pocket PC 2002 will NOT create a directory if it does not start with a leading '\'.
// PPC 2002 only accepts '\log' as a valid directory name, while 'log\' works under WinCE 3.0.
# define ACE_LOG_DIRECTORY_FOR_MKDIR ACE_TEXT ("\\log")
# define ACE_LOG_DIRECTORY           ACE_TEXT ("\\log\\")
# define MAKE_PIPE_NAME(X) ACE_TEXT ("\\\\.\\pipe\\"#X)
#elif defined (ACE_WIN32)
# define ACE_LOG_DIRECTORY ACE_TEXT ("log\\")
# define MAKE_PIPE_NAME(X) ACE_TEXT ("\\\\.\\pipe\\"#X)
#else
# define ACE_LOG_DIRECTORY ACE_TEXT ("log/")
# define MAKE_PIPE_NAME(X) ACE_TEXT (X)
#endif /* ACE_WIN32 */

#if defined (ACE_HAS_WINCE)
#define ACE_LOG_FILE_EXT_NAME ACE_TEXT (".txt")
#else
#define ACE_LOG_FILE_EXT_NAME ACE_TEXT (".log")
#endif /* ACE_HAS_WINCE */

#if defined (ACE_HAS_WINCE) || defined (ACE_HAS_PHARLAP)
const size_t ACE_MAX_CLIENTS = 4;
#else
const size_t ACE_MAX_CLIENTS = 30;
#endif /* ACE_HAS_WINCE */

const size_t ACE_NS_MAX_ENTRIES = 1000;
const size_t ACE_DEFAULT_USECS = 1000;
const size_t ACE_MAX_TIMERS = 4;
const size_t ACE_MAX_DELAY = 10;
const size_t ACE_MAX_INTERVAL = 0;
const size_t ACE_MAX_ITERATIONS = 10;
const size_t ACE_MAX_PROCESSES = 10;
const size_t ACE_MAX_THREADS = 4;

#define ACE_START_TEST(NAME) \
  const ACE_TCHAR *program = NAME; \
  ACE_LOG_MSG->open (program, ACE_Log_Msg::OSTREAM | ACE_Log_Msg::VERBOSE_LITE); \
  if (ace_file_stream::instance()->set_output (program) != 0) \
    ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set_output failed")), -1); \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Starting %s test at %D\n"), program))

#define ACE_END_TEST \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Ending %s test at %D\n"), program)); \
  ace_file_stream::instance()->close ()

#define ACE_CLOSE_TEST_LOG ace_file_stream::instance()->close ()

#if !defined (ACE_WIN32)
#define ACE_APPEND_LOG(NAME) \
  const ACE_TCHAR *program = NAME; \
  ACE_LOG_MSG->open (program, ACE_Log_Msg::OSTREAM | ACE_Log_Msg::VERBOSE_LITE); \
  ace_file_stream::instance()->close (); \
  if (ace_file_stream::instance()->set_output (program, 1) != 0) \
    ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set_output failed")), -1); \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Starting %s test at %D\n"), program));
#else /* ACE_WIN32 */
#define ACE_APPEND_LOG(NAME) \
  const ACE_TCHAR *program = NAME; \
  ACE_LOG_MSG->open (program, ACE_Log_Msg::OSTREAM | ACE_Log_Msg::VERBOSE_LITE); \
  if (ace_file_stream::instance()->set_output (program, 1) != 0) \
    ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set_output failed")), -1); \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Starting %s test at %D\n"), program));
#endif /* ACE_WIN32 */

#define ACE_END_LOG \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Ending %s test at %D\n\n"), program)); \
  ACE_LOG_MSG->set_flags(ACE_Log_Msg::SILENT); \
  ace_file_stream::instance()->close ();

#if defined (VXWORKS)
// This is the only way I could figure out to avoid an error
// about attempting to unlink a non-existant file.
#define ACE_INIT_LOG(NAME) \
  ACE_TCHAR temp[MAXPATHLEN]; \
  ACE_OS::sprintf (temp, ACE_TEXT ("%s%s%s"), \
                   ACE_LOG_DIRECTORY, \
                   ACE::basename (NAME, ACE_DIRECTORY_SEPARATOR_CHAR), \
                   ACE_LOG_FILE_EXT_NAME); \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Deleting old log file %s (if any)\n\n"), temp)); \
  int fd_init_log; \
  if ((fd_init_log = ACE_OS::open (temp, \
                                   O_WRONLY|O_CREAT, \
                                   S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) != ERROR) \
    { \
      ACE_OS::close (fd_init_log); \
      ACE_OS::unlink (temp); \
    }

#if defined (ghs)
# // Rename main to ace_main for compatibility with run_tests.vxworks.
# undef ACE_MAIN
# define ACE_MAIN ace_main
#endif /* ghs */
#else /* ! VXWORKS */
#define ACE_INIT_LOG(NAME) \
  ACE_TCHAR temp[MAXPATHLEN]; \
  ACE_OS::sprintf (temp, ACE_TEXT ("%s%s%s"), \
                   ACE_LOG_DIRECTORY, \
                   ACE::basename (NAME, ACE_DIRECTORY_SEPARATOR_CHAR), \
                   ACE_LOG_FILE_EXT_NAME); \
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) Deleting old log file %s (if any)\n\n"), temp)); \
  ACE_OS::unlink (temp);
#endif /* ! VXWORKS */

#if defined (ACE_LACKS_IOSTREAM_TOTALLY)
#define OFSTREAM FILE
#else
#define OFSTREAM ofstream
#endif /* ACE_LACKS_IOSTREAM_TOTALLY */

class ACE_Test_Output
{
public:
  ACE_Test_Output (void);
  ~ACE_Test_Output (void);
  int set_output (const ACE_TCHAR *filename, int append = 0);
  OFSTREAM *output_file (void);
  void close (void);

private:
  OFSTREAM *output_file_;
};

inline ACE_Test_Output::ACE_Test_Output (void)
  : output_file_ (0)
{
#if !defined (ACE_LACKS_IOSTREAM_TOTALLY)
  this->output_file_ = new OFSTREAM;
#endif /* ACE_LACKS_IOSTREAM_TOTALLY */
}

inline ACE_Test_Output::~ACE_Test_Output (void)
{
#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) && !defined (ACE_PSOS)
  ACE_LOG_MSG->msg_ostream (&cerr);
#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY && ! ACE_PSOS */

  ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM);
  ACE_LOG_MSG->set_flags (ACE_Log_Msg::STDERR);

#if !defined (ACE_LACKS_IOSTREAM_TOTALLY) && !defined (ACE_HAS_PHARLAP)
  delete this->output_file_;
#endif /* ! ACE_LACKS_IOSTREAM_TOTALLY */
}

inline OFSTREAM *
ACE_Test_Output::output_file (void)
{
  return this->output_file_;
}

inline int
ACE_Test_Output::set_output (const ACE_TCHAR *filename, int append)
{
#if defined (ACE_HAS_PHARLAP)
  // For PharLap, just send it all to the host console for now - redirect
  // to a file there for saving/analysis.
  EtsSelectConsole(ETS_CO_HOST);
  ACE_LOG_MSG->msg_ostream (&cout);

#else
  ACE_TCHAR temp[MAXPATHLEN];
  // Ignore the error value since the directory may already exist.
  const ACE_TCHAR *test_dir;

#if !defined (ACE_HAS_WINCE)
  test_dir = ACE_OS::getenv (ACE_TEXT ("ACE_TEST_DIR"));

  if (test_dir == 0)
#endif /* ACE_HAS_WINCE */
    test_dir = ACE_TEXT ("");

  ACE_OS::sprintf (temp,
                   ACE_TEXT ("%s%s%s%s"),
                   test_dir,
                   ACE_LOG_DIRECTORY,
                   ACE::basename (filename, ACE_DIRECTORY_SEPARATOR_CHAR),
                   ACE_LOG_FILE_EXT_NAME);

#if defined (VXWORKS)
  // This is the only way I could figure out to avoid a console
  // warning about opening an existing file (w/o O_CREAT), or
  // attempting to unlink a non-existant one.
  ACE_HANDLE fd = ACE_OS::open (temp,
                                O_WRONLY|O_CREAT,
                                S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  if (fd != ERROR)
    {
      ACE_OS::close (fd);
      ACE_OS::unlink (temp);
    }
# else /* ! VXWORKS */
  // This doesn't seem to work on VxWorks if the directory doesn't
  // exist: it creates a plain file instead of a directory.  If the
  // directory does exist, it causes a wierd console error message
  // about "cat: input error on standard input: Is a directory".  So,
  // VxWorks users must create the directory manually.
#   if defined (ACE_HAS_WINCE)
  ACE_OS::mkdir (ACE_LOG_DIRECTORY_FOR_MKDIR);
#   else
  ACE_OS::mkdir (ACE_LOG_DIRECTORY);
#   endif  // ACE_HAS_WINCE
# endif /* ! VXWORKS */

# if !defined (ACE_LACKS_IOSTREAM_TOTALLY)
  this->output_file_->open (ACE_TEXT_ALWAYS_CHAR (temp),
                            ios::out | (append ? ios::app : ios::trunc));
  if (this->output_file_->bad ())
    return -1;
#else /* when ACE_LACKS_IOSTREAM_TOTALLY */
  ACE_TCHAR *fmode = 0;
  if (append)
    fmode = ACE_TEXT ("a");
  else
    fmode = ACE_TEXT ("w");
  this->output_file_ = ACE_OS::fopen (temp, fmode);
# endif /* ACE_LACKS_IOSTREAM_TOTALLY */

  ACE_LOG_MSG->msg_ostream (this->output_file ());
#endif /* ACE_HAS_PHARLAP */

  ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR | ACE_Log_Msg::LOGGER );
  ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM);

  return 0;
}

inline void
ACE_Test_Output::close (void)
{
#if !defined (ACE_LACKS_IOSTREAM_TOTALLY)
  this->output_file_->flush ();
  this->output_file_->close ();
#else
  ACE_OS::fflush (this->output_file_);
  ACE_OS::fclose (this->output_file_);
#endif /* !ACE_LACKS_IOSTREAM_TOTALLY */
  ACE_LOG_MSG->msg_ostream (0);
}

inline void
randomize (int array[], size_t size)
{
  size_t i;

  for (i = 0; i < size; i++)
    array [i] = static_cast<int> (i);

  // See with a fixed number so that we can produce "repeatable"
  // random numbers.
  ACE_OS::srand (0);

  // Generate an array of random numbers from 0 .. size - 1.

  for (i = 0; i < size; i++)
    {
      size_t index = ACE_OS::rand() % size--;
      int temp = array [index];
      array [index] = array [size];
      array [size] = temp;
    }
}

typedef ACE_Singleton<ACE_Test_Output, ACE_Null_Mutex> ace_file_stream;

#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION)
template ACE_Singleton<ACE_Test_Output, ACE_Null_Mutex> *
ACE_Singleton<ACE_Test_Output, ACE_Null_Mutex>::singleton_;
#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */

#endif /* ACE_TEST_CONFIG_H */
