windowmaker-wl/tests/test_geom.c

309 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* test_geom.c -- display-free unit tests for wWindowConstrainSize / wWindowCropSize
*
* Links only against geom.o; no display connection required.
*
* Phase 5: verifies that WNormalHints field mapping produces the same
* geometry constraints that XSizeHints did before the abstraction.
*/
#include "wconfig.h"
#include <stdio.h>
#include <string.h>
/*
* Pull in the real headers. We compile without a display but still need
* the complete type definitions for WWindow, WScreen, WNormalHints.
*/
#include "screen.h" /* WScreen — safe without a display connection */
#include "window.h" /* WWindow, WNormalHints */
#include "geom.h"
/*
* Define the extern globals that geom.c references.
* The test links standalone so it must provide these itself.
*/
struct WPreferences wPreferences; /* zero-init; set .icon_size in main() */
/* ------------------------------------------------------------------ */
/* Test infrastructure */
/* ------------------------------------------------------------------ */
static int tests_run = 0;
static int tests_failed = 0;
#define EXPECT_EQ(got, want, label) \
do { \
tests_run++; \
int _g = (int)(got), _w = (int)(want); \
if (_g != _w) { \
tests_failed++; \
fprintf(stderr, "FAIL %s: got %d want %d\n", label, _g, _w); \
} \
} while (0)
/* Build a zero-initialised WWindow pointing at a fake WScreen */
static WScreen fake_scr;
static WNormalHints fake_hints;
static WWindow make_wwin(void)
{
WWindow w;
memset(&w, 0, sizeof(w));
w.screen_ptr = &fake_scr;
w.normal_hints = NULL;
return w;
}
/* ------------------------------------------------------------------ */
/* Tests */
/* ------------------------------------------------------------------ */
/* 1. No hints: size passes through (clamped only by screen size * 2) */
static void test_no_hints(void)
{
WWindow w = make_wwin();
unsigned int W = 500, H = 400;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 500, "no_hints width");
EXPECT_EQ(H, 400, "no_hints height");
}
/* 2. Min size: request below minimum is clamped up */
static void test_min_size(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 200;
fake_hints.min_height = 150;
fake_hints.max_width = 4000;
fake_hints.max_height = 3000;
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
w.normal_hints = &fake_hints;
unsigned int W = 100, H = 80;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 200, "min_size width clamped up");
EXPECT_EQ(H, 150, "min_size height clamped up");
}
/* 3a. Max size: only width over max → 4/3 ratio fallback */
static void test_max_size_width_only(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 1;
fake_hints.min_height = 1;
fake_hints.max_width = 300;
fake_hints.max_height = 10000; /* high enough not to trigger */
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
w.normal_hints = &fake_hints;
unsigned int W = 400, H = 300; /* W > maxW, H < maxH */
wWindowConstrainSize(&w, &W, &H);
/* Expect W = H * 4/3 = 300 * 4/3 = 400, then clamped to maxW=300 */
/* Actually: code sets width = height * 4/3 = 400, but then no further
* clamp for width > maxW in this branch. Per the code as written,
* the result is width = 400 still (4/3 of 300), which is > maxW.
* This tests the code's actual documented behaviour. */
EXPECT_EQ(W, 400, "max_width only: 4/3 ratio applied");
EXPECT_EQ(H, 300, "max_width only: height unchanged");
}
/* 3b. Max size: only height over max → 3/4 ratio fallback */
static void test_max_size_height_only(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 1;
fake_hints.min_height = 1;
fake_hints.max_width = 10000;
fake_hints.max_height = 300;
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
w.normal_hints = &fake_hints;
unsigned int W = 400, H = 500; /* H > maxH, W < maxW */
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(H, 400*3/4, "max_height only: 3/4 ratio applied");
EXPECT_EQ(W, 400, "max_height only: width unchanged");
}
/* 3c. Both dimensions over max → icon_size fallback */
static void test_max_size_both(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 1;
fake_hints.min_height = 1;
fake_hints.max_width = 200;
fake_hints.max_height = 200;
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
w.normal_hints = &fake_hints;
wPreferences.icon_size = 32;
unsigned int W = 500, H = 500;
wWindowConstrainSize(&w, &W, &H);
/* Both over max → use scr_width/scr_height - 2*icon_size */
int exp_w = fake_scr.scr_width - 2 * wPreferences.icon_size;
int exp_h = fake_scr.scr_height - 2 * wPreferences.icon_size;
EXPECT_EQ((int)W, exp_w, "both_over_max width");
EXPECT_EQ((int)H, exp_h, "both_over_max height");
}
/* 4a. Size increment: snapping to increment grid without base size */
static void test_size_inc_no_base(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 80;
fake_hints.min_height = 24;
fake_hints.max_width = 4000;
fake_hints.max_height = 3000;
fake_hints.width_inc = 8; /* e.g. terminal columns */
fake_hints.height_inc = 13; /* e.g. terminal rows */
fake_hints.base_width = 0;
fake_hints.base_height = 0;
w.normal_hints = &fake_hints;
/* Request 100×50: no base, so snaps to minW + k*inc */
/* width: (100 - 80) / 8 * 8 + 80 = 2*8 + 80 = 96 */
/* height: (50 - 24) / 13 * 13 + 24 = 2*13 + 24 = 50 */
unsigned int W = 100, H = 50;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 96, "inc_no_base width snapped");
EXPECT_EQ(H, 50, "inc_no_base height snapped");
}
/* 4b. Size increment: snapping to increment grid WITH base size */
static void test_size_inc_with_base(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.min_width = 80;
fake_hints.min_height = 24;
fake_hints.max_width = 4000;
fake_hints.max_height = 3000;
fake_hints.width_inc = 7;
fake_hints.height_inc = 14;
fake_hints.base_width = 4; /* non-zero base */
fake_hints.base_height = 4;
w.normal_hints = &fake_hints;
/* Request 110×60 with base 4, inc 7/14:
* width: (110 - 4) / 7 * 7 + 4 = 15*7 + 4 = 109
* height: (60 - 4) / 14 * 14 + 4 = 4*14 + 4 = 60 */
unsigned int W = 110, H = 60;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 109, "inc_with_base width snapped");
EXPECT_EQ(H, 60, "inc_with_base height snapped");
}
/* 5a. Aspect ratio: max aspect enforcement (too wide) */
static void test_aspect_max(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.flags = WNH_P_ASPECT;
fake_hints.min_width = 1;
fake_hints.min_height = 1;
fake_hints.max_width = 10000;
fake_hints.max_height = 10000;
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
/* max aspect 4:3 — window must not be wider than 4/3 its height */
fake_hints.min_aspect.x = 1; fake_hints.min_aspect.y = 1;
fake_hints.max_aspect.x = 4; fake_hints.max_aspect.y = 3;
w.normal_hints = &fake_hints;
/* Request 800×300 (ratio 8:3, too wide for 4:3 max) */
/* Expected: height = width*maxAY/maxAX = 800*3/4 = 600 */
unsigned int W = 800, H = 300;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 800, "max_aspect width unchanged");
EXPECT_EQ(H, 600, "max_aspect height adjusted");
}
/* 5b. Aspect ratio: min aspect enforcement (too tall) */
static void test_aspect_min(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.flags = WNH_P_ASPECT;
fake_hints.min_width = 1;
fake_hints.min_height = 1;
fake_hints.max_width = 10000;
fake_hints.max_height = 10000;
fake_hints.width_inc = 1;
fake_hints.height_inc = 1;
/* min aspect 4:3 — window must not be taller than 3/4 its width */
fake_hints.min_aspect.x = 4; fake_hints.min_aspect.y = 3;
fake_hints.max_aspect.x = 4; fake_hints.max_aspect.y = 3;
w.normal_hints = &fake_hints;
/* Request 300×800 (ratio 3:8, too tall for min 4:3) */
/* code path: minAX(4) > minAY(3), so
* height = width*minAY/minAX = 300*3/4 = 225 */
unsigned int W = 300, H = 800;
wWindowConstrainSize(&w, &W, &H);
EXPECT_EQ(W, 300, "min_aspect width unchanged");
EXPECT_EQ(H, 225, "min_aspect height adjusted");
}
/* 6. wWindowCropSize: crops each dimension independently */
static void test_crop_size(void)
{
WWindow w = make_wwin();
memset(&fake_hints, 0, sizeof(fake_hints));
fake_hints.width_inc = 8;
fake_hints.height_inc = 13;
fake_hints.base_width = 4;
fake_hints.base_height = 4;
w.normal_hints = &fake_hints;
/* maxW=300, maxH=200 */
/* width: 300 - (300-4) % 8 = 300 - 296%8 = 300 - 0 = 300 */
/* height: 200 - (200-4) % 13 = 200 - 196%13 = 200 - 196%13 */
/* 196 / 13 = 15 rem 1, so 200 - 1 = 199 */
unsigned int W = 500, H = 400;
wWindowCropSize(&w, 300, 200, &W, &H);
EXPECT_EQ(W, 300, "crop width at boundary");
EXPECT_EQ(H, 199, "crop height snapped to inc");
}
/* ------------------------------------------------------------------ */
/* main */
/* ------------------------------------------------------------------ */
int main(void)
{
/* Set screen size used as default max */
memset(&fake_scr, 0, sizeof(fake_scr));
fake_scr.scr_width = 1920;
fake_scr.scr_height = 1080;
wPreferences.icon_size = 64; /* default; overridden per test if needed */
test_no_hints();
test_min_size();
test_max_size_width_only();
test_max_size_height_only();
test_max_size_both();
test_size_inc_no_base();
test_size_inc_with_base();
test_aspect_max();
test_aspect_min();
test_crop_size();
if (tests_failed)
fprintf(stderr, "FAILED %d/%d\n", tests_failed, tests_run);
else
printf("PASS (%d tests)\n", tests_run);
return tests_failed ? 1 : 0;
}