/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "dusktest.h" #include "ui/widget/uislider.h" #include "util/memory.h" // uiSliderInitFloat/InitInt require a real font (FONT_DEFAULT) to build // the label's glyph cache, which needs a live GL context this test binary // doesn't have -- see the label size caching contract for why this is // safe to bypass here (uiSliderRebuildGeometry only reads slider->label's // already-measured width/height, it never touches the font itself). static void sliderSetupFloat( uislider_t *slider, const float_t value, const float_t min, const float_t max, const float_t step ) { memoryZero(slider, sizeof(uislider_t)); slider->label.width = 20; slider->label.height = 10; slider->type = UI_SLIDER_TYPE_FLOAT; slider->min.f = min; slider->max.f = max; slider->step.f = step; slider->value.f = value; } static void sliderSetupInt( uislider_t *slider, const int32_t value, const int32_t min, const int32_t max, const int32_t step ) { memoryZero(slider, sizeof(uislider_t)); slider->label.width = 20; slider->label.height = 10; slider->type = UI_SLIDER_TYPE_INT; slider->min.i = min; slider->max.i = max; slider->step.i = step; slider->value.i = value; } static void test_uiSliderRebuildGeometry_trackPosition(void **state) { uislider_t slider; sliderSetupFloat(&slider, 0.0f, 0.0f, 1.0f, 0.1f); uiSliderRebuildGeometry(&slider); const float_t expectedX = (float_t)slider.label.width + UI_SLIDER_GAP; const float_t expectedY = ((float_t)slider.label.height - UI_SLIDER_TRACK_HEIGHT) * 0.5f; assert_float_equal(slider.cachedTrack.min[0], expectedX, 0.0001f); assert_float_equal(slider.cachedTrack.min[1], expectedY, 0.0001f); assert_float_equal( slider.cachedTrack.max[0], expectedX + UI_SLIDER_TRACK_WIDTH, 0.0001f ); } static void test_uiSliderRebuildGeometry_fillVisibility(void **state) { uislider_t slider; // At the minimum value, the ratio is 0 -- no fill should be visible. sliderSetupFloat(&slider, 0.0f, 0.0f, 10.0f, 1.0f); uiSliderRebuildGeometry(&slider); assert_false(slider.cachedFillVisible); // Halfway to max -- fill should cover half the track. sliderSetupFloat(&slider, 5.0f, 0.0f, 10.0f, 1.0f); uiSliderRebuildGeometry(&slider); assert_true(slider.cachedFillVisible); assert_float_equal( slider.cachedFill.max[0] - slider.cachedFill.min[0], UI_SLIDER_TRACK_WIDTH * 0.5f, 0.0001f ); } static void test_uiSliderRebuildGeometry_stepMarkers(void **state) { uislider_t slider; // 5 steps between 0 and 10 -- under the marker cap, so markers render. sliderSetupInt(&slider, 0, 0, 10, 2); uiSliderRebuildGeometry(&slider); assert_int_equal(slider.cachedMarkerCount, 6); assert_float_equal( slider.cachedMarkers[0].min[0], slider.cachedTrack.min[0] - UI_SLIDER_STEP_MARKER_WIDTH * 0.5f, 0.0001f ); // 100 steps -- over the marker cap, so it falls back to a smooth fill // with no markers. sliderSetupInt(&slider, 0, 0, 100, 1); uiSliderRebuildGeometry(&slider); assert_int_equal(slider.cachedMarkerCount, 0); } int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_uiSliderRebuildGeometry_trackPosition), cmocka_unit_test(test_uiSliderRebuildGeometry_fillVisibility), cmocka_unit_test(test_uiSliderRebuildGeometry_stepMarkers), }; return cmocka_run_group_tests(tests, NULL, NULL); }