/* Copyright (C) 2018-2022  Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */

#ifndef _LIBIGLOO__TAP_H_
#define _LIBIGLOO__TAP_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <igloo/types.h>
#include <igloo/error.h> /* for igloo_ERROR_NONE */

typedef struct {
    size_t  count;
    size_t  passed;
    size_t  failed;
    int     exit_status;
} igloo_tap_stats_t;

#define igloo_TAP_EXIT_ON_FAIL      0x01U // exit on test failure
#define igloo_TAP_EXIT_ON_BAIL_OUT  0x02U // exit on bail out
#define igloo_TAP_EXIT_ON_FIN       0x04U // exit on finish
#define igloo_TAP_EXIT_AFTER_GROUP  0x80U // delay exit to end of group (or finish)

/* initialize a TAP test session */
igloo_error_t   igloo_tap_init(void);
/* finishes a TAP test session.
 * This might also exit the process if igloo_TAP_EXIT_ON_FIN is set
 */
igloo_error_t   igloo_tap_fin(void);

/* configure automatic exit of process.
 *
 * Parameters:
 *  flags
 *      Bitwise or of igloo_TAP_EXIT_* flags
 *  cleanup
 *      Function to run before exit is called.
 *      This function is called with the test system still in init-ed state
 *      to allow calls to igloo_tap_diagnostic(), and igloo_tap_dump_*().
 */
igloo_error_t   igloo_tap_exit_on(unsigned int flags, void (*cleanup)(void));

/* Perform a basic test
 *
 * Parameters:
 *  desc
 *      Description/name of the test.
 *  res
 *      Whether the test succeeded or not.
 */
igloo_error_t   igloo_tap_test(const char *desc, bool res);

/* Test for a function to return success.
 *
 * Parameters:
 *  desc
 *      Description/name of the test.
 *  got
 *      The error code returned (one of igloo_ERROR_*).
 */
#define         igloo_tap_test_success(desc, got) igloo_tap_test_error((desc), igloo_ERROR_NONE, (got))

/* Test for a specific error code.
 *
 * Parameters:
 *  desc
 *      Description/name of the test.
 *  expected
 *      The error code expected (one of igloo_ERROR_*).
 *  got
 *      The error code got (one of igloo_ERROR_*).
 */
igloo_error_t   igloo_tap_test_error(const char *desc, igloo_error_t expected, igloo_error_t got);

/* Writes a comment into the TAP log.
 * Such comments can be useful to provide more information about a specific test or group of tests.
 *
 * Parameters:
 *  line
 *      The message to write.
 */
igloo_error_t   igloo_tap_diagnostic(const char *line);

/* Performs a bail out.
 * After a bail out testing should not continue.
 *
 * Parameters:
 *  reason
 *      The reason why the bail out happened.
 *
 * See also:
 *  * igloo_tap_can_continue(),
 *  * igloo_TAP_EXIT_ON_BAIL_OUT.
 */
igloo_error_t   igloo_tap_bail_out(const char *reason);

/* Checks whether testing can continue */
bool            igloo_tap_can_continue(igloo_error_t *error);

/* Marks the beginning of a group of tests.
 *
 * Using igloo_tap_group_run() is preferable.
 *
 * Parameters:
 *  group
 *      Name of the group.
 *
 * See also:
 *  * igloo_tap_group_end()
 *  * igloo_tap_group_run().
 */
igloo_error_t   igloo_tap_group_begin(const char *group);

/* Marks the end of a group of tests. */
igloo_error_t   igloo_tap_group_end(void);

/* Runs a function as a seperate group of tests.
 *
 * The group is only tested if testing can continue.
 *
 * Parameters:
 *  group
 *      Name of the group.
 *  func
 *      The function to run.
 *
 * See also:
 *  * igloo_tap_can_continue().
 */
igloo_error_t   igloo_tap_group_run(const char *group, void (*func)(void));

// useful helpers:
/* Dumps an ro object as diagnostic.
 *
 * This function dumps an ro object as a set of diagnostic lines.
 *
 * Using igloo_tap_dump_ro_variable() is preferable.
 *
 * Parameters:
 *  object
 *      The object to dump.
 *  name
 *      The name of the object.
 *
 * See also:
 *  * igloo_tap_dump_ro_variable().
 */
igloo_error_t   igloo_tap_dump_ro(igloo_ro_t object, const char *name);

/* Dumps an ro object variable as diagnostic.
 *
 * This is the same as igloo_tap_dump_ro() but uses the name of the given variable
 * as name.
 *
 * Parameters:
 *  object
 *      The object to dump.
 *
 * See also:
 *  * igloo_tap_dump_ro().
 */
#define         igloo_tap_dump_ro_variable(var) igloo_tap_dump_ro((var), # var);

// statistics:
/* Gets current statistic about the tests.
 *
 * Parameters:
 *  stats
 *      Pointer to an igloo_tap_stats_t object to write to.
 */
#define         igloo_tap_get_stats(stats) igloo_tap_get_stats_real((stats), sizeof(igloo_tap_stats_t))

/* Internal function, DO NOT call directly. */
igloo_error_t   igloo_tap_get_stats_real(igloo_tap_stats_t *stats, size_t len);

#ifdef __cplusplus
}
#endif

#endif /* ! _LIBIGLOO__TAP_H_ */
