/* ========================================================================== */
/*! \file
 * \brief Test of \c cmpr_send() and \c cmpr_recv() implementation
 *
 * Copyright (c) 2015-2020 by the developers. See the LICENSE file for details.
 */


/* ========================================================================== */
/* Include headers */

#include "posix.h"  /* Include this first because of feature test macros */

#include <stdio.h>
#include <string.h>

#include "config.h"

#include "compression.h"
#include "fileutils.h"
#include "inet.h"
#include "test.h"
#include "test_compression.h"


/* ========================================================================== */
/*! \addtogroup TEST */
/*! @{ */


#if !CFG_CMPR_DISABLE


/*! \brief Use real network connection to echo daemon instead of regular file */
#  define TEST_CMPR_USE_NETWORK  0


#  if !TEST_CMPR_USE_NETWORK


/* ========================================================================== */
/* Emulation of \c posix_send() */

static ssize_t  test_send(int  sd, const void*  buf, size_t  len, int  flags)
{
   return(posix_write(sd, buf, len));
}


/* ========================================================================== */
/* Emulation \c posix_recv() */

static ssize_t  test_recv(int  sd, void*  buf, size_t  len, int  flags)
{
   posix_ssize_t  res = -1;

   if(flags)
   {
      posix_errno = POSIX_EINVAL;
      print_error("test_recv(): Flags not supported");
   }
   else  { res = posix_read(sd, buf, len); }

   return(res);
}


#  endif  /* TEST_CMPR_USE_NETWORK */


/* ========================================================================== */
/*! \brief Test \c cmpr_send() and \c cmpr_recv() implementation
 *
 * The following cases are tested:
 * - Loopback test with DEFLATE algorithm
 *
 * \return
 * - \c EXIT_SUCCESS on success
 * - \c EXIT_FAILURE on error
 */

int  test_compression(void)
{
#  define BUF_LEN  (size_t) 1023  /* Maximum length of strings */
#  define TS_NUM  (size_t) 6  /* Number of test strings */
   static const char*  ts[TS_NUM] =
   {
      "CAPABILITIES",

      "ARTICLE 123",

      "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRlc3Qgc3RyaW5nCg==",

      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",

      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",

      ""
   };
   int  res = POSIX_EXIT_SUCCESS;
#  if TEST_CMPR_USE_NETWORK
   const char*  echo_host = "localhost";
   int  af = POSIX_AF_UNSPEC;
#  else  /* TEST_CMPR_USE_NETWORK */
   const char*  pathname = "/tmp/flnews_loopb";
   int  testfile_open = 0;
   posix_off_t  rv3;
#  endif  /* TEST_CMPR_USE_NETWORK */
   int  sd = -1;
   char  buf[BUF_LEN + (size_t) 1];
   size_t  i;
   const char*  loc;
   int  rv;
   posix_ssize_t  rv2;
   void*  cs = NULL;
   size_t  len;

#  if TEST_CMPR_USE_NETWORK
   /* Use echo service as loopback facility */
   rv = inet_connect(&sd, &af, echo_host, "echo");
   if(rv)
   {
      print_error("This test requires an echo daemon running on localhost");
      res = POSIX_EXIT_FAILURE;
   }
#  else  /* TEST_CMPR_USE_NETWORK */
   /* Use regular file as loopback facility */
   rv = fu_open_file(pathname, &sd,
                     POSIX_O_RDWR | POSIX_O_CREAT | POSIX_O_TRUNC,
                     TEST_PERM);
   if(!rv)
   {
      rv = fu_lock_file(sd);
      if(rv)  { fu_close_file(&sd, NULL); }
   }
   if(rv)
   {
      print_error("Creating or locking test file failed");
      res = POSIX_EXIT_FAILURE;
   }
   else  { testfile_open = 1; }
#  endif  /* TEST_CMPR_USE_NETWORK */

   /* Create compressed data stream */
   if(POSIX_EXIT_SUCCESS == res)
   {
      cs = cmpr_stream_constructor(CMPR_ALG_DEFLATE, sd,
#  if TEST_CMPR_USE_NETWORK
                                   posix_send, posix_recv);
#  else  /* TEST_CMPR_USE_NETWORK */
                                   test_send, test_recv);
#  endif  /* TEST_CMPR_USE_NETWORK */
      if(NULL == cs)  { res = POSIX_EXIT_FAILURE; }
   }

   /* Loopback test */
   if(POSIX_EXIT_SUCCESS == res)
   {
      for(i = 0; i < TS_NUM; ++i)
      {
         len = strlen(ts[i]);
         ++len;  /* Transfer string with NUL termination */
         if(BUF_LEN < len)
         {
            print_error("Length of test string too long");
            res = POSIX_EXIT_FAILURE;
            break;
         }
#  if !TEST_CMPR_USE_NETWORK
#     if CFG_USE_POSIX_API >= 200112 || CFG_USE_XSI
         /* Truncate test file */
         rv = posix_ftruncate(sd, (posix_off_t) 0);
         if(rv)
         {
            print_error("Truncation of test file failed");
            res = POSIX_EXIT_FAILURE;
            break;
         }
#     endif  /* CFG_USE_POSIX_API >= 200112 || CFG_USE_XSI */
         /* Reset offset of test file to zero */
         rv3 = posix_lseek(sd, (posix_off_t) 0, POSIX_SEEK_SET);
         if((posix_off_t) -1 == rv3)
         {
            print_error("Seek in test file failed");
            res = POSIX_EXIT_FAILURE;
            break;
         }
#  endif  /* ! TEST_CMPR_USE_NETWORK */
         /* Send */
         rv2 = cmpr_send(cs, ts[i], len);
         if(0 >= rv2)
         {
            res = POSIX_EXIT_FAILURE;
            break;
         }
         /* Flush */
         rv = cmpr_flush(cs);
         if(rv)
         {
            res = POSIX_EXIT_FAILURE;
            break;
         }
#  if !TEST_CMPR_USE_NETWORK
         /* Rewind test file offset to zero */
         rv3 = posix_lseek(sd, (posix_off_t) 0, POSIX_SEEK_SET);
         if((posix_off_t) -1 == rv3)
         {
            print_error("Seek in test file failed");
            res = POSIX_EXIT_FAILURE;
            break;
         }
#  endif  /* ! TEST_CMPR_USE_NETWORK */
         /* Receive */
         rv2 = cmpr_recv(cs, buf, len, 0);
         if(0 >= rv2)
         {
            res = POSIX_EXIT_FAILURE;
            break;
         }
         /* Compare */
         if(strcmp(ts[i], buf))
         {
            print_error("Result is not correct");
            /* For debugging */
#  if CFG_USE_POSIX_API >= 200112 || CFG_USE_XSI
            loc = posix_setlocale(POSIX_LC_CTYPE, "");
#  else  /* CFG_USE_POSIX_API >= 200112 || CFG_USE_XSI */
            loc = NULL;
#  endif  /* CFG_USE_POSIX_API >= 200112 || CFG_USE_XSI */
            if(NULL == loc)
            {
               print_error("Setting locale for debug messages failed");
            }
            else
            {
               /* Print Unicode data only if terminal use Unicode locale */
               if(NULL == strstr(loc, "UTF") && NULL == strstr(loc, "utf"))
               {
                  print_error(
                     "Debug messages can't be printed with current locale");
               }
               else
               {
                  fprintf(stderr, TEST_TAB "Data sent    : \"%s\"\n", ts[i]);
                  fprintf(stderr, TEST_TAB "Data received: \"%s\"\n", buf);
               }
            }
            res = POSIX_EXIT_FAILURE;
            break;
         }
      }
   }

#  if TEST_CMPR_USE_NETWORK
   /* Close loopback connection */
   if(-1 != sd)  { inet_close(&sd); }
#  else  /* TEST_CMPR_USE_NETWORK */
   /* Close and delete loopback regular file */
   if(testfile_open)
   {
      fu_close_file(&sd, NULL);
      (void) fu_unlink_file(pathname);
   }
#  endif  /* TEST_CMPR_USE_NETWORK */

   /* Destroy compressed data stream */
   cmpr_stream_destructor(cs);

   return(res);
}


#endif  /* !CFG_CMPR_DISABLE */


/*! @} */

/* EOF */
