/**
 * @file version.c
 * @brief Unit tests for the version macros and the linked-library accessor.
 *
 * These assert that the several places the version appears cannot drift: the
 * string, the numeric components, and what the shared library reports at
 * runtime all come from one project() call, and this is what proves it.
 */

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

#include <akgl/error.h>
#include <akgl/version.h>

#include "testutil.h"

/**
 * @brief The version string and the numeric components must describe one version.
 *
 * They are separate substitutions in version.h.in, so a mangled template can
 * leave them disagreeing and nothing else would notice.
 */
akerr_ErrorContext *test_version_string_matches_components(void)
{
    PREPARE_ERROR(e);
    char assembled[64];

    ATTEMPT {
	snprintf(assembled, sizeof(assembled), "%d.%d.%d",
		 AKGL_VERSION_MAJOR, AKGL_VERSION_MINOR, AKGL_VERSION_PATCH);
	TEST_ASSERT(e, strcmp(assembled, AKGL_VERSION) == 0,
		    "AKGL_VERSION is \"%s\" but the components assemble to \"%s\"",
		    AKGL_VERSION, assembled);
    } CLEANUP {
    } PROCESS(e) {
    } FINISH(e, true);
    SUCCEED_RETURN(e);
}

/**
 * @brief The linked library must report the version its headers were generated from.
 *
 * In this build tree they are the same tree, so this can only fail if the test
 * picked up an installed libakgl off LD_LIBRARY_PATH instead of the one just
 * built -- which is precisely the mispairing the accessor exists to catch.
 */
akerr_ErrorContext *test_version_linked_matches_compiled(void)
{
    PREPARE_ERROR(e);

    ATTEMPT {
	TEST_ASSERT(e, akgl_version() != NULL, "akgl_version returned NULL");
	TEST_ASSERT(e, strcmp(akgl_version(), AKGL_VERSION) == 0,
		    "linked libakgl reports \"%s\" but the headers say \"%s\"",
		    akgl_version(), AKGL_VERSION);
    } CLEANUP {
    } PROCESS(e) {
    } FINISH(e, true);
    SUCCEED_RETURN(e);
}

/**
 * @brief AKGL_VERSION_AT_LEAST must order versions correctly at the boundaries.
 *
 * Checked against the current version rather than fixed literals, so the test
 * does not have to be rewritten every time the version is bumped.
 */
akerr_ErrorContext *test_version_at_least_boundaries(void)
{
    PREPARE_ERROR(e);

    ATTEMPT {
	TEST_ASSERT(e,
		    AKGL_VERSION_AT_LEAST(AKGL_VERSION_MAJOR, AKGL_VERSION_MINOR, AKGL_VERSION_PATCH),
		    "AKGL_VERSION_AT_LEAST rejected the current version");
	TEST_ASSERT(e,
		    !AKGL_VERSION_AT_LEAST(AKGL_VERSION_MAJOR, AKGL_VERSION_MINOR, AKGL_VERSION_PATCH + 1),
		    "AKGL_VERSION_AT_LEAST accepted a later patch");
	TEST_ASSERT(e,
		    !AKGL_VERSION_AT_LEAST(AKGL_VERSION_MAJOR, AKGL_VERSION_MINOR + 1, 0),
		    "AKGL_VERSION_AT_LEAST accepted a later minor");
	TEST_ASSERT(e,
		    !AKGL_VERSION_AT_LEAST(AKGL_VERSION_MAJOR + 1, 0, 0),
		    "AKGL_VERSION_AT_LEAST accepted a later major");
	TEST_ASSERT(e,
		    AKGL_VERSION_AT_LEAST(AKGL_VERSION_MAJOR, AKGL_VERSION_MINOR, 0),
		    "AKGL_VERSION_AT_LEAST rejected an earlier patch");
    } CLEANUP {
    } PROCESS(e) {
    } FINISH(e, true);
    SUCCEED_RETURN(e);
}

int main(void)
{
    PREPARE_ERROR(errctx);

    ATTEMPT {
	CATCH(errctx, akgl_error_init());
	CATCH(errctx, test_version_string_matches_components());
	CATCH(errctx, test_version_linked_matches_compiled());
	CATCH(errctx, test_version_at_least_boundaries());
    } CLEANUP {
    } PROCESS(errctx) {
    } FINISH_NORETURN(errctx);
}
