Files
coven/modules/ufbx/test/unit_tests.c

305 lines
8.0 KiB
C

#include "../ufbx.c"
#include "../ufbx.h"
#include <stdio.h>
#include <string.h>
void test_assert(bool cond)
{
if (!cond) {
exit(1);
}
}
typedef struct {
uint32_t a, b;
} uint_pair;
static size_t g_linear_size = 2;
void sort_uints(uint32_t *test_data, uint32_t *test_tmp, size_t test_size)
{
ufbxi_macro_stable_sort(uint32_t, g_linear_size, test_data, test_tmp, test_size, (*a < *b));
}
void sort_pairs_by_a(uint_pair *data, uint_pair *tmp, size_t size)
{
ufbxi_macro_stable_sort(uint_pair, g_linear_size, data, tmp, size, (a->a < b->a));
}
void sort_pairs_by_b(uint_pair *data, uint_pair *tmp, size_t size)
{
ufbxi_macro_stable_sort(uint_pair, g_linear_size, data, tmp, size, (a->b < b->b));
}
size_t find_uint(uint32_t *test_data, size_t test_size, uint32_t value)
{
size_t index = SIZE_MAX;
ufbxi_macro_lower_bound_eq(uint32_t, g_linear_size, &index, test_data, 0, test_size,
(*a < value),
(*a == value));
return index;
}
size_t find_uint_end(uint32_t *test_data, size_t test_size, size_t test_begin, uint32_t value)
{
size_t index = SIZE_MAX;
ufbxi_macro_upper_bound_eq(uint32_t, g_linear_size, &index, test_data, test_begin, test_size, (*a == value));
return index;
}
size_t find_pair_by_a(uint_pair *data, size_t size, uint32_t value)
{
size_t pair_ix = SIZE_MAX;
ufbxi_macro_lower_bound_eq(uint_pair, g_linear_size, &pair_ix, data, 0, size,
(a->a < value),
(a->a == value));
return pair_ix;
}
void sort_strings(const char **data, const void *tmp, size_t size)
{
ufbxi_macro_stable_sort(const char*, g_linear_size, data, tmp, size, (strcmp(*a, *b) < 0));
}
size_t find_first_string(const char **data, size_t size, const char *str)
{
size_t str_index;
ufbxi_macro_lower_bound_eq(const char*, g_linear_size, &str_index, data, 0, size,
(strcmp(*a, str) < 0),
(strcmp(*a, str) == 0));
return str_index;
}
static uint32_t xorshift32(uint32_t *state)
{
/* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
uint32_t x = *state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return *state = x;
}
static ufbx_real xorshift32_real(uint32_t *state)
{
uint32_t u = xorshift32(state);
return (ufbx_real)u * (ufbx_real)2.3283064365386962890625e-10;
}
void generate_linear(uint32_t *dst, size_t size, uint32_t start, uint32_t delta)
{
for (size_t i = 0; i < size; i++) {
dst[i] = start;
start += delta;
}
}
void generate_random(uint32_t *dst, size_t size, uint32_t seed, uint32_t mod)
{
uint32_t state = seed | 1;
for (size_t i = 0; i < size; i++) {
dst[i] = xorshift32(&state) % mod;
}
}
#define MAX_SORT_SIZE 2048
ufbx_real quat_error(ufbx_quat a, ufbx_quat b)
{
double pos = fabs(a.x-b.x) + fabs(a.y-b.y) + fabs(a.z-b.z) + fabs(a.w-b.w);
double neg = fabs(a.x+b.x) + fabs(a.y+b.y) + fabs(a.z+b.z) + fabs(a.w+b.w);
return pos < neg ? pos : neg;
}
void test_quats()
{
uint32_t state = 1;
for (int iorder = 0; iorder < 6; iorder++) {
ufbx_rotation_order order = (ufbx_rotation_order)iorder;
printf("quat_to_euler %d/6\n", iorder+1);
for (int axis = 0; axis < 3; axis++) {
for (size_t i = 1; i <= 360; i++) {
ufbx_vec3 v = { 0.0f, 0.0f, 0.0f };
v.v[axis] = (ufbx_real)i;
ufbx_quat q = ufbx_euler_to_quat(v, order);
ufbx_vec3 v2 = ufbx_quat_to_euler(q, order);
ufbx_quat q2 = ufbx_euler_to_quat(v2, order);
test_assert(quat_error(q, q2) < 0.001f);
test_assert(quat_error(q, q2) < 0.001f);
}
}
for (int x = -360; x <= 360; x += 10)
for (int y = -360; y <= 360; y += 10)
for (int z = -360; z <= 360; z += 10) {
ufbx_vec3 v = { (ufbx_real)x, (ufbx_real)y, (ufbx_real)z };
ufbx_quat q = ufbx_euler_to_quat(v, order);
ufbx_vec3 v2 = ufbx_quat_to_euler(q, order);
ufbx_quat q2 = ufbx_euler_to_quat(v2, order);
test_assert(quat_error(q, q2) < 0.1f);
test_assert(quat_error(q, q2) < 0.1f);
}
for (size_t i = 0; i < 1000000; i++) {
ufbx_quat q;
q.x = xorshift32_real(&state) * 2.0f - 1.0f;
q.y = xorshift32_real(&state) * 2.0f - 1.0f;
q.z = xorshift32_real(&state) * 2.0f - 1.0f;
q.w = xorshift32_real(&state) * 2.0f - 1.0f;
ufbx_real qm = (ufbx_real)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
q.x /= qm;
q.y /= qm;
q.z /= qm;
q.w /= qm;
ufbx_vec3 v = ufbx_quat_to_euler(q, order);
ufbx_quat q2 = ufbx_euler_to_quat(v, order);
test_assert(
fabs(q.x-q2.x) + fabs(q.y-q2.y) + fabs(q.z-q2.z) + fabs(q.w-q2.w) < 0.001f ||
fabs(q.x+q2.x) + fabs(q.y+q2.y) + fabs(q.z+q2.z) + fabs(q.w+q2.w) < 0.001f );
test_assert(true);
}
}
}
void test_sort(uint32_t *data, size_t size)
{
assert(size <= MAX_SORT_SIZE);
static size_t call_count = 0;
static uint32_t uint_tmp_buffer[MAX_SORT_SIZE];
static uint_pair pair_buffer[MAX_SORT_SIZE];
static uint_pair pair_tmp_buffer[MAX_SORT_SIZE];
call_count++;
for (size_t i = 0; i < size; i++) {
pair_buffer[i].a = data[i];
pair_buffer[i].b = (uint32_t)i;
}
sort_uints(data, uint_tmp_buffer, size);
sort_pairs_by_a(pair_buffer, pair_tmp_buffer, size);
for (size_t i = 1; i < size; i++) {
test_assert(data[i - 1] <= data[i]);
test_assert(pair_buffer[i - 1].a <= pair_buffer[i].a);
if (pair_buffer[i - 1].a == pair_buffer[i].a) {
test_assert(pair_buffer[i - 1].b < pair_buffer[i].b);
}
}
for (size_t i = 0; i < size; i++) {
uint32_t value = data[i];
size_t index = find_uint(data, size, value);
test_assert(index <= i);
test_assert(data[index] == value);
test_assert(index == find_pair_by_a(pair_buffer, size, value));
if (index > 0) {
test_assert(data[index - 1] < value);
}
size_t end = find_uint_end(data, size, index, value);
test_assert(end > i);
test_assert(data[end - 1] == value);
if (end < size) {
test_assert(data[end] > value);
}
}
test_assert(find_uint(data, size, UINT32_MAX) == SIZE_MAX);
sort_pairs_by_b(pair_buffer, pair_tmp_buffer, size);
for (size_t i = 0; i < size; i++) {
test_assert(pair_buffer[i].b == (uint32_t)i);
}
}
void test_sort_strings(uint32_t *data, size_t size)
{
assert(size <= MAX_SORT_SIZE);
static size_t call_count = 0;
static const char *str_buffer[MAX_SORT_SIZE];
static const char *str_tmp_buffer[MAX_SORT_SIZE];
static char str_data_buffer[MAX_SORT_SIZE * 32];
char *data_ptr = str_data_buffer, *data_end = data_ptr + sizeof(str_data_buffer);
for (size_t i = 0; i < size; i++) {
int len = snprintf(data_ptr, data_end - data_ptr, "%u", data[i]);
test_assert(len > 0);
str_buffer[i] = data_ptr;
data_ptr += len + 1;
}
sort_strings(str_buffer, str_tmp_buffer, size);
for (size_t i = 1; i < size; i++) {
test_assert(strcmp(str_buffer[i - 1], str_buffer[i]) <= 0);
}
char find_str[128];
for (size_t i = 0; i < size; i++) {
int len = snprintf(find_str, sizeof(find_str), "%u", data[i]);
test_assert(len > 0);
size_t index = find_first_string(str_buffer, size, find_str);
test_assert(index < size);
test_assert(strcmp(str_buffer[index], find_str) == 0);
if (index > 0) {
test_assert(strcmp(str_buffer[index - 1], find_str) < 0);
}
}
}
void test_sorts()
{
static uint32_t sort_buffer[MAX_SORT_SIZE];
while (g_linear_size <= 64) {
printf("%zu\n", g_linear_size);
for (size_t size = 0; size < MAX_SORT_SIZE; size += 1+ size/128 + size/512*32) {
generate_linear(sort_buffer, size, 0, +1);
test_sort(sort_buffer, size);
generate_linear(sort_buffer, size, (uint32_t)size, -1);
test_sort(sort_buffer, size);
generate_random(sort_buffer, size, (uint32_t)size, 1+size%10);
test_sort(sort_buffer, size);
generate_random(sort_buffer, size, (uint32_t)size, UINT32_MAX);
test_sort(sort_buffer, size);
}
{
size_t size = MAX_SORT_SIZE;
generate_linear(sort_buffer, size, 0, +1);
test_sort_strings(sort_buffer, size);
generate_linear(sort_buffer, size, (uint32_t)size, -1);
test_sort_strings(sort_buffer, size);
generate_random(sort_buffer, size, (uint32_t)size, 1+size%10);
test_sort_strings(sort_buffer, size);
generate_random(sort_buffer, size, (uint32_t)size, UINT32_MAX);
test_sort_strings(sort_buffer, size);
}
g_linear_size += 1+g_linear_size/8;
}
}
int main(int argc, char **argv)
{
test_sorts();
test_quats();
return 0;
}