/*******************************************************************************
* We use libcheck(https://github.com/libcheck/check) as unit testing framework.

* compile postgres first with different crc32c implementation(use arm crc32c
* and vmull intrisics or not). we should comment out some codes about elog in
* pg_crc32c_armv8_choose.c to compile correctly and simply.
* $ gcc -I ../postgres/_install/include -I ../postgres/_install/include/server \
  crc32c_unittest.c  -L ../postgres/build/src/port -l pgport_srv  -L /usr/local/lib \
  -lcheck  -o crc32c_unittest

* this test was run on Neoverse-N1
* $ ./crc32c_unittest 
* Running suite(s): CRC32C
* 100%: Checks: 3, Failures: 0, Errors: 0
*******************************************************************************/
#include <stdlib.h>
#include <check.h>

#include "c.h"
#include "port/pg_crc32c.h"

START_TEST (test_crc32c_0)
{
    int crc = 0;

    int data = 0;

    INIT_CRC32C(crc);
    COMP_CRC32C(crc, &data, sizeof(int));
    FIN_CRC32C(crc);
    ck_assert_int_eq(crc, 0x48674bc7);
}
END_TEST

START_TEST (test_crc32c_small_size)
{
    int crc = 0;

    int size = 512;
    uint8_t *buf = (uint8_t*)malloc(size * sizeof(uint8_t));
    memset(buf, 0, size * sizeof(uint8_t));

    INIT_CRC32C(crc);
    COMP_CRC32C(crc, buf, size * sizeof(uint8_t));
    FIN_CRC32C(crc);
    ck_assert_int_eq(crc, 0x30fcedc0);
    
    free(buf);
}
END_TEST

START_TEST (test_crc32c_large_size)
{
    int crc = 0;

    int size = 4096;
    uint8_t *buf = (uint8_t*)malloc(size * sizeof(uint8_t));
    for (int i = 0; i < size; i++) {
        *(buf + i) |= 0xFF;
    }

    INIT_CRC32C(crc);
    COMP_CRC32C(crc, buf, size * sizeof(uint8_t));
    FIN_CRC32C(crc);
    ck_assert_int_eq(crc, 0x25c1fe13);
    
    free(buf);
}
END_TEST


Suite * crc32c_suite(void)
{
    Suite *s;
    TCase *tc_core;

    s = suite_create("CRC32C");

    /* Core test case */
    tc_core = tcase_create("Core");

    tcase_add_test(tc_core, test_crc32c_0);
    tcase_add_test(tc_core, test_crc32c_small_size);
    tcase_add_test(tc_core, test_crc32c_large_size);
    suite_add_tcase(s, tc_core);

    return s;
}

int main()
{
    int number_failed;
    Suite *s;
    SRunner *sr;

    s = crc32c_suite();
    sr = srunner_create(s);

    srunner_run_all(sr, CK_NORMAL);
    number_failed = srunner_ntests_failed(sr);
    srunner_free(sr);
    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

