/*                                                                              *
 *   This file is part of the ESO IRPLIB package                                *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library is free software; you can redistribute it and/or modify       *
 *   it under the terms of the GNU General Public License as published by       *
 *   the Free Software Foundation; either version 2 of the License, or          *
 *   (at your option) any later version.                                        *
 *                                                                              *
 *   This program 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 General Public License for more details.                               *
 *                                                                              *
 *   You should have received a copy of the GNU General Public License          *
 *   along with this program; if not, write to the Free Software                *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */
 
#ifndef TEST_H
#define TEST_H

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/

#include <cpl.h>

/*-----------------------------------------------------------------------------
                                Defines
 -----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief  Evaluate an expression and update an internal counter if it fails
  @param  expr   The (boolean) expression to evaluate, side-effects are allowed
  @note   A zero value of the expression is a failure, other values are not
  @return void

  Example of usage:
   @code

   test(myfunc(&p));
   test(p != NULL);
   
   @endcode

 */
/*----------------------------------------------------------------------------*/
#define test(expr) \
        test_macro(expr, #expr, cpl_func, __FILE__, __LINE__)

/*----------------------------------------------------------------------------*/
/**
  @brief  Test if two integer expressions are equal
  @param  first      The first value in the comparison, side-effects are allowed
  @param  second     The second value in the comparison, side-effects are allowed

  @see test()
  @return void

  Example of usage:
   @code

   test_eq(computed, expected);
   
   @endcode

  For comparison of floating point values, see test_abs() and
  test_rel().

 */
/*----------------------------------------------------------------------------*/
#define test_eq(first, second)                                          \
        test_eq_macro(first, #first, second, #second,                   \
                              cpl_func, __FILE__, __LINE__)
/*----------------------------------------------------------------------------*/
/**
  @brief  Test if two strings are equal
  @param  first      The first string in the comparison
  @param  second     The second string in the comparison

  If or two NULL pointer(s) is considered a failure.

  Example of usage:
   @code

   test_eq_string(computed, expected);
   
   @endcode

 */
/*----------------------------------------------------------------------------*/
#define test_eq_string(first, second)                                   \
        test_eq_string_macro(first, #first, second, #second,            \
                              cpl_func, __FILE__, __LINE__)

/*----------------------------------------------------------------------------*/
/**
  @brief  Test if two numerical expressions are within a given absolute tolerance
  @param  first      The first value in the comparison, side-effects are allowed
  @param  second     The second value in the comparison, side-effects are allowed
  @param  tolerance  A non-negative tolerance
  @note If the tolerance is negative, the test will always fail

  @see test()
  @return void

  Example of usage:
   @code

   test_abs(computed, expected, DBL_EPSILON);
   
   @endcode

 */
/*----------------------------------------------------------------------------*/
#define test_abs(first, second, tolerance)                              \
        test_abs_macro(first, #first, second, #second,                  \
                              tolerance, #tolerance,                           \
                              cpl_func, __FILE__, __LINE__)

/*----------------------------------------------------------------------------*/
/**
  @brief  Test if two numerical expressions are within a given relative tolerance
  @param  first      The first value in the comparison, side-effects are allowed
  @param  second     The second value in the comparison, side-effects are allowed
  @param  tolerance  A non-negative tolerance
  @note   If the tolerance is negative, the test will always fail
  @see test()
  @return void

  The values
      abs [ (@em first - @em second)/(@em first) ] 
  and
      abs [ (@em first - @em second)/(@em second) ] 
  are compared with the given @em tolerance. The test is therefore symmetrical 
  in the the first and second arguments.

  Example of usage:
   @code

   test_rel(computed, expected, 0.001);
   
   @endcode

 */
/*----------------------------------------------------------------------------*/
#define test_rel(first, second, tolerance)                              \
        test_rel_macro(first, #first, second, #second,                  \
                              tolerance, #tolerance,                           \
                              cpl_func, __FILE__, __LINE__)

/*----------------------------------------------------------------------------*/
/**
  @brief Initialize CPL + CPL messaging + IRPLIB + unit test
  @note  This macro should be used at the beginning of main() 
 */
/*----------------------------------------------------------------------------*/
#define TEST_INIT test_init_macro(__FILE__)

/*----------------------------------------------------------------------------*/
/**
  @brief  End unit test
  @note   This macro should be used at the end of main() 
 */
/*----------------------------------------------------------------------------*/
#define TEST_END \
    return(test_end_macro(cpl_func, __FILE__, __LINE__) ? \
    EXIT_FAILURE : EXIT_SUCCESS)


/*-----------------------------------------------------------------------------
                               Functions prototypes
 -----------------------------------------------------------------------------*/
/* None of these functions should be called directly */
void
test_init_macro(const char *file);

unsigned
test_end_macro(const char *function, const char *file, unsigned line);

void 
test_macro(int expression, const char *expr_string,
                  const char *function, const char *file, unsigned line);

void 
test_eq_macro(int first,  const char *first_string,
                     int second, const char *second_string,
                     const char *function, const char *file, unsigned line);
void 
test_eq_string_macro(const char *first,  const char *first_string,
			    const char *second, const char *second_string,
			    const char *function, 
			    const char *file, unsigned line);
void 
test_abs_macro(double first,  const char *first_string,
                      double second, const char *second_string,
                      double tolerance, const char *tolerance_string,
                      const char *function, const char *file, unsigned line);

void 
test_rel_macro(double first,  const char *first_string,
                      double second, const char *second_string,
                      double tolerance, const char *tolerance_string,
                      const char *function, const char *file, unsigned line);

void 
test_recipe_output(const cpl_frameset *frames,
                   const char *const product_tags[], int n_prod,
		   const char *main_product,
		   const char *const qc[], int n_qc);

/**@}*/

#endif
