309 lines
11 KiB
C
309 lines
11 KiB
C
/*
|
||
* 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;
|
||
}
|