prosperon/source/engine/thirdparty/sokol/tests/functional/sokol_spine_test.c

1198 lines
42 KiB
C

//------------------------------------------------------------------------------
// sokol_spine_test.c
//------------------------------------------------------------------------------
#include "sokol_gfx.h"
#define SOKOL_SPINE_IMPL
#include "spine/spine.h"
#include "sokol_spine.h"
#include "utest.h"
#define T(b) EXPECT_TRUE(b)
static sspine_log_item last_logitem = SSPINE_LOGITEM_OK;
static void log_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) {
(void)tag; (void)log_level; (void)message; (void)line_nr; (void)filename; (void)user_data;
last_logitem = log_item;
}
static void init(void) {
last_logitem = SSPINE_LOGITEM_OK;
sg_setup(&(sg_desc){0});
sspine_setup(&(sspine_desc){ .logger = { .func = log_func } });
}
static void init_with_desc(const sspine_desc* desc) {
last_logitem = SSPINE_LOGITEM_OK;
sspine_desc desc1 = *desc;
desc1.logger.func = log_func;
sg_setup(&(sg_desc){0});
sspine_setup(&desc1);
}
static void shutdown(void) {
sspine_shutdown();
sg_shutdown();
}
// NOTE: this guarantees that the data is zero terminated because the loaded data
// might either be binary or text (the zero sentinel is NOT counted in the returned size)
static sspine_range load_data(const char* path) {
assert(path);
FILE* fp = fopen(path, "rb");
assert(fp);
fseek(fp, 0, SEEK_END);
const size_t size = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
// room for terminating zero
const size_t alloc_size = size + 1;
uint8_t* ptr = (uint8_t*)malloc(alloc_size);
memset(ptr, 0, alloc_size);
// NOTE: GCC warns if result of fread() is ignored
size_t num_bytes = fread(ptr, size, 1, fp);
(void)num_bytes;
fclose(fp);
return (sspine_range) { .ptr = ptr, .size = size };
}
static void free_data(sspine_range r) {
free((void*)r.ptr);
}
static sspine_atlas create_atlas(void) {
sspine_range atlas_data = load_data("spineboy.atlas");
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
.data = atlas_data
});
free_data(atlas_data);
return atlas;
}
static sspine_skeleton create_skeleton_json(sspine_atlas atlas) {
sspine_range skeleton_json_data = load_data("spineboy-pro.json");
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.atlas = atlas,
.json_data = (const char*)skeleton_json_data.ptr
});
free_data(skeleton_json_data);
return skeleton;
}
static sspine_skeleton create_skeleton_binary(sspine_atlas atlas) {
sspine_range skeleton_binary_data = load_data("spineboy-pro.skel");
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.atlas = atlas,
.binary_data = skeleton_binary_data
});
free_data(skeleton_binary_data);
return skeleton;
}
static sspine_skeleton create_skeleton(void) {
return create_skeleton_json(create_atlas());
}
static sspine_instance create_instance(void) {
return sspine_make_instance(&(sspine_instance_desc){
.skeleton = create_skeleton(),
});
}
UTEST(sokol_spine, default_init_shutdown) {
// FIXME!
T(true);
}
UTEST(sokol_spine, atlas_pool_exhausted) {
init_with_desc(&(sspine_desc){
.atlas_pool_size = 4,
});
for (int i = 0; i < 4; i++) {
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
}
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
T(SSPINE_INVALID_ID == atlas.id);
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID);
T(last_logitem == SSPINE_LOGITEM_ATLAS_POOL_EXHAUSTED);
shutdown();
}
UTEST(sokol_spine, make_destroy_atlas_ok) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_VALID);
T(sspine_atlas_valid(atlas));
sspine_destroy_atlas(atlas);
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID);
T(!sspine_atlas_valid(atlas))
shutdown();
}
UTEST(sokol_spine, make_atlas_fail_no_data) {
init();
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
T(atlas.id != SSPINE_INVALID_ID);
T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
T(!sspine_atlas_valid(atlas));
shutdown();
}
// an invalid atlas must return zero number of images
UTEST(sokol_spine, failed_atlas_no_images) {
init();
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
T(atlas.id != SSPINE_INVALID_ID);
T(!sspine_atlas_valid(atlas));
T(sspine_num_images(atlas) == 0);
shutdown();
}
// NOTE: spine-c doesn't detect wrong/corrupt atlas file data, so we can't test for that
UTEST(sokol_spine, image_valid) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_image_valid(sspine_image_by_index(atlas, 0)));
T(!sspine_image_valid(sspine_image_by_index(atlas, 1)));
T(!sspine_image_valid(sspine_image_by_index(atlas, -1)));
sspine_destroy_atlas(atlas);
T(!sspine_image_valid(sspine_image_by_index(atlas, 0)));
shutdown();
}
UTEST(sokol_spine, atlas_image_info) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_atlas_valid(atlas));
T(sspine_num_images(atlas) == 1);
const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0));
T(img_info.valid);
T(img_info.sgimage.id != SG_INVALID_ID);
T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC);
T(strcmp(img_info.filename.cstr, "spineboy.png") == 0);
T(img_info.min_filter == SG_FILTER_LINEAR);
T(img_info.mag_filter == SG_FILTER_LINEAR);
T(img_info.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
T(img_info.width == 1024);
T(img_info.height == 256);
T(img_info.premul_alpha == false);
shutdown();
}
UTEST(sokol_spine, atlas_with_overrides) {
init();
sspine_range atlas_data = load_data("spineboy.atlas");
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
.data = atlas_data,
.override = {
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
.mipmap_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_REPEAT,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.premul_alpha_enabled = true,
}
});
T(sspine_atlas_valid(atlas));
T(sspine_num_images(atlas) == 1);
const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0));
T(img_info.valid);
T(img_info.sgimage.id != SG_INVALID_ID);
T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC);
T(strcmp(img_info.filename.cstr, "spineboy.png") == 0);
T(img_info.min_filter == SG_FILTER_NEAREST);
T(img_info.mag_filter == SG_FILTER_NEAREST);
T(img_info.mipmap_filter == SG_FILTER_LINEAR);
T(img_info.wrap_u == SG_WRAP_REPEAT);
T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
T(img_info.width == 1024);
T(img_info.height == 256);
T(img_info.premul_alpha == true);
shutdown();
}
UTEST(sokol_spine, skeleton_pool_exhausted) {
init_with_desc(&(sspine_desc){
.skeleton_pool_size = 4
});
for (int i = 0; i < 4; i++) {
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
}
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
T(SSPINE_INVALID_ID == skeleton.id);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
T(last_logitem == SSPINE_LOGITEM_SKELETON_POOL_EXHAUSTED);
shutdown();
}
UTEST(sokol_spine, make_destroy_skeleton_json_ok) {
init();
sspine_skeleton skeleton = create_skeleton_json(create_atlas());
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID);
T(sspine_skeleton_valid(skeleton));
sspine_destroy_skeleton(skeleton);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
T(!sspine_skeleton_valid(skeleton));
shutdown();
}
UTEST(sokol_spine, make_destroy_skeleton_binary_ok) {
init();
sspine_skeleton skeleton = create_skeleton_binary(create_atlas());
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID);
T(sspine_skeleton_valid(skeleton));
sspine_destroy_skeleton(skeleton);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
T(!sspine_skeleton_valid(skeleton));
shutdown();
}
UTEST(sokol_spine, make_skeleton_fail_no_data) {
init();
sspine_atlas atlas = create_atlas();
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.atlas = atlas
});
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
T(!sspine_skeleton_valid(skeleton));
T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
shutdown();
}
UTEST(sokol_spine, make_skeleton_fail_no_atlas) {
init();
sspine_range skeleton_json_data = load_data("spineboy-pro.json");
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.json_data = (const char*)skeleton_json_data.ptr
});
free_data(skeleton_json_data);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
T(!sspine_skeleton_valid(skeleton));
T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_ATLAS);
shutdown();
}
UTEST(sokol_spine, make_skeleton_fail_with_failed_atlas) {
init();
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
sspine_skeleton skeleton = create_skeleton_json(atlas);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
T(!sspine_skeleton_valid(skeleton));
T(last_logitem == SSPINE_LOGITEM_SKELETON_ATLAS_NOT_VALID);
shutdown();
}
UTEST(sokol_spine, make_skeleton_json_fail_corrupt_data) {
init();
sspine_atlas atlas = create_atlas();
const char* invalid_json_data = "This is not valid JSON!";
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.atlas = atlas,
.json_data = (const char*)invalid_json_data,
});
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_CREATE_SKELETON_DATA_FROM_JSON_FAILED);
sspine_destroy_skeleton(skeleton);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
shutdown();
}
// FIXME: this crashes the spine-c runtime
/*
UTEST(sokol_spine, make_skeleton_binary_fail_corrupt_data) {
init();
sspine_atlas atlas = create_atlas();
uint8_t invalid_binary_data[] = { 0x23, 0x63, 0x11, 0xFF };
sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
.atlas = atlas,
.binary_data = { .ptr = invalid_binary_data, .size = sizeof(invalid_binary_data) }
});
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
sspine_destroy_skeleton(skeleton);
T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
shutdown();
}
*/
UTEST(sokol_spine, instance_pool_exhausted) {
init_with_desc(&(sspine_desc){
.instance_pool_size = 4
});
for (int i = 0; i < 4; i++) {
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON);
}
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
T(SSPINE_INVALID_ID == instance.id);
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
T(last_logitem == SSPINE_LOGITEM_INSTANCE_POOL_EXHAUSTED);
shutdown();
}
UTEST(sokol_spine, make_destroy_instance_ok) {
init();
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
.skeleton = create_skeleton_json(create_atlas())
});
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_VALID);
T(sspine_instance_valid(instance));
sspine_destroy_instance(instance);
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
T(!sspine_instance_valid(instance));
shutdown();
}
UTEST(sokol_spine, make_instance_fail_no_skeleton) {
init();
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON);
sspine_destroy_instance(instance);
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
shutdown();
}
UTEST(sokol_spine, make_instance_fail_with_failed_skeleton) {
init();
sspine_skeleton failed_skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
T(sspine_get_skeleton_resource_state(failed_skeleton) == SSPINE_RESOURCESTATE_FAILED);
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
.skeleton = failed_skeleton
});
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_INSTANCE_SKELETON_NOT_VALID);
shutdown();
}
UTEST(sokol_spine, make_instance_fail_with_destroyed_atlas) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_atlas_valid(atlas));
sspine_skeleton skeleton = create_skeleton_json(atlas);
T(sspine_skeleton_valid(skeleton));
sspine_destroy_atlas(atlas);
T(!sspine_atlas_valid(atlas));
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
.skeleton = skeleton
});
T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
T(last_logitem == SSPINE_LOGITEM_INSTANCE_ATLAS_NOT_VALID);
shutdown();
}
UTEST(sokol_spine, get_skeleton_atlas) {
init();
sspine_atlas atlas = create_atlas();
sspine_skeleton skeleton = create_skeleton_json(atlas);
T(sspine_get_skeleton_atlas(skeleton).id == atlas.id);
sspine_destroy_skeleton(skeleton);
T(sspine_get_skeleton_atlas(skeleton).id == SSPINE_INVALID_ID);
shutdown();
}
UTEST(sokol_spine, get_instance_skeleton) {
init();
sspine_atlas atlas = create_atlas();
sspine_skeleton skeleton = create_skeleton_json(atlas);
sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
.skeleton = skeleton
});
T(sspine_get_instance_skeleton(instance).id == skeleton.id);
sspine_destroy_instance(instance);
T(sspine_get_instance_skeleton(instance).id == SSPINE_INVALID_ID);
shutdown();
}
UTEST(sokol_spine, set_get_position) {
init();
sspine_instance instance = create_instance();
sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f });
const sspine_vec2 pos = sspine_get_position(instance);
T(pos.x == 1.0f);
T(pos.y == 2.0f);
shutdown();
}
UTEST(sokol_spine, set_get_position_destroyed_instance) {
init();
sspine_instance instance = create_instance();
sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f });
sspine_destroy_instance(instance);
const sspine_vec2 pos = sspine_get_position(instance);
T(pos.x == 0.0f);
T(pos.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_scale) {
init();
sspine_instance instance = create_instance();
sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f });
const sspine_vec2 scale = sspine_get_scale(instance);
T(scale.x == 2.0f);
T(scale.y == 3.0f);
shutdown();
}
UTEST(sokol_spine, set_get_scale_destroyed_instance) {
init();
sspine_instance instance = create_instance();
sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f });
sspine_destroy_instance(instance);
const sspine_vec2 scale = sspine_get_scale(instance);
T(scale.x == 0.0f);
T(scale.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_color) {
init();
sspine_instance instance = create_instance();
sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f });
const sspine_color color = sspine_get_color(instance);
T(color.r == 1.0f);
T(color.g == 2.0f);
T(color.b == 3.0f);
T(color.a == 4.0f);
shutdown();
}
UTEST(sokol_spine, set_get_color_destroyed_instance) {
init();
sspine_instance instance = create_instance();
sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f });
sspine_destroy_instance(instance);
const sspine_color color = sspine_get_color(instance);
T(color.r == 0.0f);
T(color.g == 0.0f);
T(color.b == 0.0f);
T(color.a == 0.0f);
shutdown();
}
UTEST(sokol_spine, anim_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard");
T((a0.skeleton_id == skeleton.id) && (a0.index == 2));
sspine_anim a1 = sspine_anim_by_name(skeleton, "bla");
T((a1.skeleton_id == 0) && (a1.index == 0));
shutdown();
}
UTEST(sokol_spine, anim_by_name_destroyed_instance) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard");
T((a0.skeleton_id == 0) && (a0.index == 0));
shutdown();
}
UTEST(sokol_spine, anim_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_anim_valid(sspine_anim_by_index(skeleton, 0)));
T(sspine_anim_valid(sspine_anim_by_index(skeleton, 10)));
T(!sspine_anim_valid(sspine_anim_by_index(skeleton, -1)));
T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 11)));
sspine_destroy_skeleton(skeleton);
T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, anim_equal) {
init();
T(sspine_anim_equal((sspine_anim){ 1, 2 }, (sspine_anim){ 1, 2 }));
T(!sspine_anim_equal((sspine_anim){ 2, 2 }, (sspine_anim){ 1, 2 }));
T(!sspine_anim_equal((sspine_anim){ 1, 3 }, (sspine_anim){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, num_anims) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_anims(skeleton) == 11);
sspine_destroy_skeleton(skeleton);
T(sspine_num_anims(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, get_anim_info) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard");
const sspine_anim_info info = sspine_get_anim_info(anim);
T(info.valid);
T(info.index == 2);
T(strcmp(info.name.cstr, "hoverboard") == 0);
T(info.duration == 1.0f);
shutdown();
}
UTEST(sokol_spine, get_anim_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard");
sspine_destroy_skeleton(skeleton);
const sspine_anim_info info = sspine_get_anim_info(anim);
T(!info.valid);
shutdown();
}
UTEST(sokol_spine, get_anim_info_invalid_index) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_anim_info i0 = sspine_get_anim_info(sspine_anim_by_index(skeleton, -1));
T(!i0.valid);
T(!i0.name.valid);
const sspine_anim_info i1 = sspine_get_anim_info(sspine_anim_by_index(skeleton, 1234));
T(!i1.valid);
T(!i1.name.valid);
shutdown();
}
UTEST(sokol_spine, atlas_page_valid) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0)));
T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, -1)));
T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 1)));
sspine_destroy_atlas(atlas);
T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0)));
shutdown();
}
UTEST(sokol_spine, num_atlas_pages) {
init();
sspine_atlas atlas = create_atlas();
T(sspine_num_atlas_pages(atlas) == 1);
sspine_destroy_atlas(atlas);
T(sspine_num_atlas_pages(atlas) == 0);
shutdown();
}
UTEST(sokol_spine, get_atlas_page_info) {
init();
sspine_atlas atlas = create_atlas();
const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
T(info.valid);
T(info.atlas.id == atlas.id);
T(info.image.valid);
T(info.image.sgimage.id != SG_INVALID_ID);
T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC);
T(strcmp(info.image.filename.cstr, "spineboy.png") == 0);
T(info.image.min_filter == SG_FILTER_LINEAR);
T(info.image.mag_filter == SG_FILTER_LINEAR);
T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
T(info.image.width == 1024);
T(info.image.height == 256);
T(info.image.premul_alpha == false);
T(info.overrides.min_filter == _SG_FILTER_DEFAULT);
T(info.overrides.mag_filter == _SG_FILTER_DEFAULT);
T(info.overrides.wrap_u == _SG_WRAP_DEFAULT);
T(info.overrides.wrap_v == _SG_WRAP_DEFAULT);
T(!info.overrides.premul_alpha_enabled);
T(!info.overrides.premul_alpha_disabled);
shutdown();
}
UTEST(sokol_spine, get_atlas_page_info_destroyed_atlas) {
init();
sspine_atlas atlas = create_atlas();
sspine_destroy_atlas(atlas);
const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
T(!info.valid);
T(info.atlas.id == SSPINE_INVALID_ID);
shutdown();
}
UTEST(sokol_spine, get_atlas_page_info_invalid_index) {
init();
sspine_atlas atlas = create_atlas();
sspine_destroy_atlas(atlas);
const sspine_atlas_page_info i0 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, -1));
T(!i0.valid);
T(i0.atlas.id == SSPINE_INVALID_ID);
const sspine_atlas_page_info i1 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 1234));
T(!i0.valid);
T(i1.atlas.id == SSPINE_INVALID_ID);
shutdown();
}
UTEST(sokol_spine, atlas_get_atlas_page_info_with_overrides) {
init();
sspine_range atlas_data = load_data("spineboy.atlas");
sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
.data = atlas_data,
.override = {
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
.mipmap_filter = SG_FILTER_NEAREST,
.wrap_u = SG_WRAP_REPEAT,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.premul_alpha_enabled = true,
}
});
const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
T(info.valid);
T(info.atlas.id == atlas.id);
T(info.image.valid);
T(info.image.sgimage.id != SG_INVALID_ID);
T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC);
T(strcmp(info.image.filename.cstr, "spineboy.png") == 0);
T(info.image.min_filter == SG_FILTER_LINEAR);
T(info.image.mag_filter == SG_FILTER_LINEAR);
T(info.image.mipmap_filter == SG_FILTER_NONE);
T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
T(info.image.width == 1024);
T(info.image.height == 256);
T(info.image.premul_alpha == true); // FIXME: hmm, this is actually inconsistent
T(info.overrides.min_filter == SG_FILTER_NEAREST);
T(info.overrides.mag_filter == SG_FILTER_NEAREST);
T(info.overrides.mipmap_filter == SG_FILTER_NEAREST);
T(info.overrides.wrap_u == SG_WRAP_REPEAT);
T(info.overrides.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
T(info.overrides.premul_alpha_enabled);
T(!info.overrides.premul_alpha_disabled);
shutdown();
}
UTEST(sokol_spine, bone_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair");
T((b0.skeleton_id == skeleton.id) && (b0.index == 2));
sspine_bone b1 = sspine_bone_by_name(skeleton, "blablub");
T((b1.skeleton_id == 0) && (b1.index == 0));
shutdown();
}
UTEST(sokol_spine, bone_by_name_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair");
T((b0.skeleton_id == 0) && (b0.index == 0));
shutdown();
}
UTEST(sokol_spine, bone_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_bone_valid(sspine_bone_by_index(skeleton, 0)));
T(sspine_bone_valid(sspine_bone_by_index(skeleton, 66)));
T(!sspine_bone_valid(sspine_bone_by_index(skeleton, -1)));
T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 67)));
sspine_destroy_skeleton(skeleton);
T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, bone_equal) {
init();
T(sspine_bone_equal((sspine_bone){ 1, 2 }, (sspine_bone){ 1, 2 }));
T(!sspine_bone_equal((sspine_bone){ 2, 2 }, (sspine_bone){ 1, 2 }));
T(!sspine_bone_equal((sspine_bone){ 1, 3 }, (sspine_bone){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, num_bones) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_bones(skeleton) == 67);
sspine_destroy_skeleton(skeleton);
T(sspine_num_bones(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, get_bone_info_root) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "root"));
T(info.valid);
T(info.index == 0);
T((info.parent_bone.skeleton_id == 0) && (info.parent_bone.index == 0));
T(strcmp(info.name.cstr, "root") == 0);
T(info.length == 0.0f);
T(info.pose.position.x == 0.0f);
T(info.pose.position.y == 0.0f);
T(info.pose.rotation == 0.05f);
T(info.pose.scale.x == 1.0f);
T(info.pose.scale.y == 1.0f);
T(info.pose.shear.x == 0.0f);
T(info.pose.shear.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, get_bone_info_parent_bone) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "rear-shin"));
T(info.valid);
T(info.index == 7);
T((info.parent_bone.skeleton_id == skeleton.id) && (info.parent_bone.index == 6));
shutdown();
}
UTEST(sokol_spine, get_bone_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_destroy_skeleton(skeleton);
const sspine_bone_info info = sspine_get_bone_info(bone);
T(!info.valid);
T(!info.name.valid);
shutdown();
}
UTEST(sokol_spine, get_bone_info_invalid_index) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_bone_info i0 = sspine_get_bone_info(sspine_bone_by_index(skeleton, -1));
T(!i0.valid);
T(!i0.name.valid);
const sspine_bone_info i1 = sspine_get_bone_info(sspine_bone_by_index(skeleton, 1234));
T(!i1.valid);
T(!i1.name.valid);
shutdown();
}
UTEST(sokol_spine, set_get_bone_transform) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){
.position = { 1.0f, 2.0f },
.rotation = 3.0f,
.scale = { 4.0f, 5.0f },
.shear = { 6.0f, 7.0f }
});
const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone);
T(tform.position.x == 1.0f);
T(tform.position.y == 2.0f);
T(tform.rotation == 3.0f);
T(tform.scale.x == 4.0f);
T(tform.scale.y == 5.0f);
T(tform.shear.x == 6.0f);
T(tform.shear.y == 7.0f);
shutdown();
}
UTEST(sokol_spine, set_get_bone_transform_destroyed_instance) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_destroy_instance(instance);
sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){
.position = { 1.0f, 2.0f },
.rotation = 3.0f,
.scale = { 4.0f, 5.0f },
.shear = { 6.0f, 7.0f }
});
const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone);
T(tform.position.x == 0.0f);
T(tform.position.y == 0.0f);
T(tform.rotation == 0.0f);
T(tform.scale.x == 0.0f);
T(tform.scale.y == 0.0f);
T(tform.shear.x == 0.0f);
T(tform.shear.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_bone_position) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_set_bone_position(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
const sspine_vec2 p0 = sspine_get_bone_position(instance, bone);
T(p0.x == 1.0f);
T(p0.y == 2.0f);
sspine_destroy_instance(instance);
const sspine_vec2 p1 = sspine_get_bone_position(instance, bone);
T(p1.x == 0.0f);
T(p1.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_bone_rotation) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_set_bone_rotation(instance, bone, 5.0f);
T(sspine_get_bone_rotation(instance, bone) == 5.0f);
sspine_destroy_instance(instance);
T(sspine_get_bone_rotation(instance, bone) == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_bone_scale) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_set_bone_scale(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
const sspine_vec2 s0 = sspine_get_bone_scale(instance, bone);
T(s0.x == 1.0f);
T(s0.y == 2.0f);
sspine_destroy_instance(instance);
const sspine_vec2 s1 = sspine_get_bone_scale(instance, bone);
T(s1.x == 0.0f);
T(s1.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, set_get_bone_shear) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_bone bone = sspine_bone_by_name(skeleton, "root");
sspine_set_bone_shear(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
const sspine_vec2 s0 = sspine_get_bone_shear(instance, bone);
T(s0.x == 1.0f);
T(s0.y == 2.0f);
sspine_destroy_instance(instance);
const sspine_vec2 s1 = sspine_get_bone_shear(instance, bone);
T(s1.x == 0.0f);
T(s1.y == 0.0f);
shutdown();
}
UTEST(sokol_spine, slot_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1");
T((s0.skeleton_id == skeleton.id) && (s0.index == 3));
sspine_slot s1 = sspine_slot_by_name(skeleton, "blablub");
T((s1.skeleton_id == 0) && (s1.index == 0));
shutdown();
}
UTEST(sokol_spine, slot_by_name_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1");
T((s0.skeleton_id == 0) && (s0.index == 0));
shutdown();
}
UTEST(sokol_spine, num_slots) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_slots(skeleton) == 52);
sspine_destroy_skeleton(skeleton);
T(sspine_num_slots(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, slot_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_slot_valid(sspine_slot_by_index(skeleton, 0)));
T(sspine_slot_valid(sspine_slot_by_index(skeleton, 51)));
T(!sspine_slot_valid(sspine_slot_by_index(skeleton, -1)));
T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 52)));
sspine_destroy_skeleton(skeleton);
T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, slot_equal) {
init();
T(sspine_slot_equal((sspine_slot){ 1, 2 }, (sspine_slot){ 1, 2 }));
T(!sspine_slot_equal((sspine_slot){ 2, 2 }, (sspine_slot){ 1, 2 }));
T(!sspine_slot_equal((sspine_slot){ 1, 3 }, (sspine_slot){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, get_slot_info) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_slot_info info = sspine_get_slot_info(sspine_slot_by_name(skeleton, "portal-streaks1"));
T(info.valid);
T(info.index == 3);
T(strcmp(info.name.cstr, "portal-streaks1") == 0);
T(!info.attachment_name.valid);
T((info.bone.skeleton_id == skeleton.id) && (info.bone.index == 62));
T(info.color.r == 1.0f);
T(info.color.g == 1.0f);
T(info.color.b == 1.0f);
T(info.color.a == 1.0f);
shutdown();
}
UTEST(sokol_spine, get_slot_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1");
sspine_destroy_skeleton(skeleton);
const sspine_slot_info info = sspine_get_slot_info(slot);
T(!info.valid);
T(!info.name.valid);
shutdown();
}
UTEST(sokol_spine, get_slot_info_invalid_index) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_slot_info i0 = sspine_get_slot_info(sspine_slot_by_index(skeleton, -1));
T(!i0.valid);
T(!i0.name.valid);
const sspine_slot_info i1 = sspine_get_slot_info(sspine_slot_by_index(skeleton, 1234));
T(!i1.valid);
T(!i1.name.valid);
shutdown();
}
UTEST(sokol_spine, set_get_slot_color) {
init();
sspine_instance instance = create_instance();
sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1");
sspine_set_slot_color(instance, slot, (sspine_color){ 1.0f, 2.0f, 3.0f, 4.0f });
const sspine_color color = sspine_get_slot_color(instance, slot);
T(color.r == 1.0f);
T(color.g == 2.0f);
T(color.b == 3.0f);
T(color.a == 4.0f);
const sspine_slot_info info = sspine_get_slot_info(slot);
T(info.color.r == 1.0f);
T(info.color.g == 1.0f);
T(info.color.b == 1.0f);
T(info.color.a == 1.0f);
shutdown();
}
UTEST(sokol_spine, event_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_event e0 = sspine_event_by_name(skeleton, "footstep");
T((e0.skeleton_id == skeleton.id) && (e0.index == 0));
sspine_event e1 = sspine_event_by_name(skeleton, "bla");
T((e1.skeleton_id == 0) && (e1.index == 0));
shutdown();
}
UTEST(sokol_spine, event_by_name_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
sspine_event e0 = sspine_event_by_name(skeleton, "footstep");
T((e0.skeleton_id == 0) && (e0.index == 0));
shutdown();
}
UTEST(sokol_spine, event_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_event_valid(sspine_event_by_index(skeleton, 0)));
T(!sspine_event_valid(sspine_event_by_index(skeleton, 1)));
T(!sspine_event_valid(sspine_event_by_index(skeleton, -1)));
sspine_destroy_skeleton(skeleton);
T(!sspine_event_valid(sspine_event_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, event_equal) {
init();
T(sspine_event_equal((sspine_event){ 1, 2 }, (sspine_event){ 1, 2 }));
T(!sspine_event_equal((sspine_event){ 2, 2 }, (sspine_event){ 1, 2 }));
T(!sspine_event_equal((sspine_event){ 1, 3 }, (sspine_event){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, num_events) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_events(skeleton) == 1);
sspine_destroy_skeleton(skeleton);
T(sspine_num_events(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, get_event_info) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0));
T(info.valid);
T(0 == strcmp(info.name.cstr, "footstep"));
T(0 == info.index);
T(0 == info.int_value);
T(0.0f == info.float_value);
T(!info.string_value.valid);
T(!info.audio_path.valid);
T(0.0f == info.volume);
T(0.0f == info.balance);
shutdown();
}
UTEST(sokol_spine, get_event_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0));
T(!info.valid);
T(!info.name.valid);
shutdown();
}
UTEST(sokol_spine, iktarget_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik");
T((ik0.skeleton_id == skeleton.id) && (ik0.index == 2));
sspine_iktarget ik1 = sspine_iktarget_by_name(skeleton, "bla");
T((ik1.skeleton_id == 0) && (ik1.index == 0));
shutdown();
}
UTEST(sokol_spine, iktarget_by_name_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik");
T((ik0.skeleton_id == 0) && (ik0.index == 0));
shutdown();
}
UTEST(sokol_spine, iktarget_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0)));
T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 6)));
T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, -1)));
T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 7)));
sspine_destroy_skeleton(skeleton);
T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, iktarget_equal) {
init();
T(sspine_iktarget_equal((sspine_iktarget){ 1, 2 }, (sspine_iktarget){ 1, 2 }));
T(!sspine_iktarget_equal((sspine_iktarget){ 2, 2 }, (sspine_iktarget){ 1, 2 }));
T(!sspine_iktarget_equal((sspine_iktarget){ 1, 3 }, (sspine_iktarget){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, num_iktargets) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_iktargets(skeleton) == 7);
sspine_destroy_skeleton(skeleton);
T(sspine_num_iktargets(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, get_iktarget_info) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1));
T(info.valid);
T(1 == info.index);
T(0 == strcmp(info.name.cstr, "aim-torso-ik"));
T((info.target_bone.skeleton_id == skeleton.id) && (info.target_bone.index == 2));
shutdown();
}
UTEST(sokol_spine, get_iktarget_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1));
T(!info.valid);
T(!info.name.valid);
shutdown();
}
UTEST(sokol_spine, get_iktarget_info_out_of_bounds) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
const sspine_iktarget_info info0 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, -1));
T(!info0.name.valid);
const sspine_iktarget_info info1 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 7));
T(!info1.name.valid);
shutdown();
}
UTEST(sokol_spine, skin_by_name) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_skin s0 = sspine_skin_by_name(skeleton, "default");
T((s0.skeleton_id == skeleton.id) && (s0.index == 0));
sspine_skin s1 = sspine_skin_by_name(skeleton, "bla");
T((s1.skeleton_id == 0) && (s1.index == 0));
sspine_destroy_skeleton(skeleton);
sspine_skin s2 = sspine_skin_by_name(skeleton, "default");
T((s2.skeleton_id == 0) && (s2.index == 0));
shutdown();
}
UTEST(sokol_spine, skin_valid) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_skin_valid(sspine_skin_by_index(skeleton, 0)));
T(!sspine_skin_valid(sspine_skin_by_index(skeleton, -1)));
T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 1)));
sspine_destroy_skeleton(skeleton);
T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 0)));
shutdown();
}
UTEST(sokol_spine, skin_equal) {
init();
T(sspine_skin_equal((sspine_skin){ 1, 2 }, (sspine_skin){ 1, 2 }));
T(!sspine_skin_equal((sspine_skin){ 2, 2 }, (sspine_skin){ 1, 2 }));
T(!sspine_skin_equal((sspine_skin){ 1, 3 }, (sspine_skin){ 1, 2 }));
shutdown();
}
UTEST(sokol_spine, num_skins) {
init();
sspine_skeleton skeleton = create_skeleton();
T(sspine_num_skins(skeleton) == 1);
sspine_destroy_skeleton(skeleton);
T(sspine_num_skins(skeleton) == 0);
shutdown();
}
UTEST(sokol_spine, get_skin_info) {
init();
sspine_skeleton skeleton = create_skeleton();
const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0));
T(info.valid);
T(0 == info.index);
T(0 == strcmp(info.name.cstr, "default"));
shutdown();
}
UTEST(sokol_spine, get_skin_info_destroyed_skeleton) {
init();
sspine_skeleton skeleton = create_skeleton();
sspine_destroy_skeleton(skeleton);
const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0));
T(!info.valid);
T(!info.name.valid);
shutdown();
}