Add test target and widget unit tests

This commit is contained in:
Brendan Szymanski 2026-06-19 20:14:53 -04:00
parent 3a54e96d8b
commit 284a2c4842
2 changed files with 196 additions and 0 deletions

View file

@ -0,0 +1,187 @@
import Adwaita
import CAdw
import CAspectContainer
import Testing
// MARK: - Helpers
/// Initialize GTK once per test suite.
func ensureGTKInit() {
struct Once {
static let initialized: Bool = {
gtk_init()
return true
}()
}
_ = Once.initialized
}
/// Create a simple test widget that reports a fixed size.
func makeFixedChild(width: Int, height: Int) -> UnsafeMutablePointer<GtkWidget>? {
let box = gtk_box_new(.GTK_ORIENTATION_VERTICAL, 0)
gtk_widget_set_size_request(box, Int32(width), Int32(height))
gtk_widget_realize(box)
return box
}
/// Measure a widget and return (minimum, natural).
func measureWidget(
_ widget: UnsafeMutablePointer<GtkWidget>?,
orientation: GtkOrientation,
forSize: Int = -1
) -> (minimum: Int, natural: Int) {
var min: Int32 = 0
var nat: Int32 = 0
gtk_widget_measure(widget, orientation, Int32(forSize), &min, &nat, nil, nil)
return (Int(min), Int(nat))
}
// MARK: - AspectContainer Tests
@Suite("Widget Tests", .serialized)
struct WidgetTests {
init() {
ensureGTKInit()
}
@Test("Reports height = width × ratio when for_size is known")
func measureWithKnownWidth() {
let container = aspect_container_new(2.0)!
let child = makeFixedChild(width: 100, height: 50)
gtk_widget_set_parent(child, container)
let (minH, natH) = measureWidget(
container, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 100)
#expect(minH == 200, "height = 100 × 2.0 = 200")
#expect(natH == 200, "natural height = 200")
}
@Test("Reports height = child_min_w × ratio when for_size is unknown")
func measureFallback() {
let container = aspect_container_new(1.5)!
let child = makeFixedChild(width: 100, height: 100)
gtk_widget_set_parent(child, container)
gtk_widget_set_size_request(container, 100, -1)
let (minH, natH) = measureWidget(
container, orientation: .GTK_ORIENTATION_VERTICAL, forSize: -1)
#expect(minH == 150, "height = 100 × 1.5 = 150 (minimum)")
#expect(natH == 150, "natural = 150")
}
@Test("max_width caps reported width")
func maxWidthCapsWidth() {
let container = aspect_container_new(1.5)!
let child = makeFixedChild(width: 300, height: 300)
gtk_widget_set_parent(child, container)
aspect_container_set_max_width(container.opaque(), 200)
let (minW, natW) = measureWidget(
container, orientation: .GTK_ORIENTATION_HORIZONTAL, forSize: -1)
#expect(minW == 200, "capped at 200")
#expect(natW == 200, "capped at 200")
}
@Test("max_width affects height calculation")
func maxWidthAffectsHeight() {
let container = aspect_container_new(2.0)!
let child = makeFixedChild(width: 300, height: 150)
gtk_widget_set_parent(child, container)
aspect_container_set_max_width(container.opaque(), 100)
let (minH, natH) = measureWidget(
container, orientation: .GTK_ORIENTATION_VERTICAL, forSize: -1)
#expect(minH == 200, "height = max_width × ratio = 200")
#expect(natH == 200, "natural = 200")
}
@Test("Changing aspect ratio triggers resize")
func changeAspectRatio() {
let container = aspect_container_new(1.0)!
let child = makeFixedChild(width: 100, height: 100)
gtk_widget_set_parent(child, container)
aspect_container_set_aspect_ratio(container.opaque(), 0.5)
let (minH, natH) = measureWidget(
container, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 100)
#expect(minH == 50, "height = 100 × 0.5 = 50")
#expect(natH == 50, "natural = 50")
}
@Test("Horizontal measure returns 0 without max_width")
func horizontalMeasureNoMaxWidth() {
let container = aspect_container_new(1.5)!
let child = makeFixedChild(width: 300, height: 200)
gtk_widget_set_parent(child, container)
let (minW, natW) = measureWidget(
container, orientation: .GTK_ORIENTATION_HORIZONTAL, forSize: -1)
#expect(minW == 0, "no max_width → reports 0")
#expect(natW == 0, "no max_width → reports 0")
}
// MARK: - AspectContainer Tests
/// Helper: create a FlowGrid with `n` children, each having a given size.
func makeFlowGrid(
minimumSize: Int = 100,
columnSpacing: Int = 0,
rowSpacing: Int = 0,
childSize: Int = 100,
count: Int = 4
) -> UnsafeMutablePointer<GtkWidget>? {
let grid = flow_grid_new(Int32(minimumSize), Int32(columnSpacing), Int32(rowSpacing))
for _ in 0..<count {
let child = gtk_box_new(.GTK_ORIENTATION_VERTICAL, 0)
gtk_widget_set_size_request(child, Int32(childSize), Int32(childSize))
gtk_widget_set_parent(child, grid)
}
return grid
}
@Test("Height = tallest child height when for_size is known")
func measureTalliesRowHeights() {
let grid = makeFlowGrid(
minimumSize: 150, columnSpacing: 0, rowSpacing: 0, childSize: 100, count: 6)
let (minH, natH) = measureWidget(grid, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 300)
#expect(minH == 300, "3 rows × 100 = 300")
#expect(natH == 300, "natural = 300")
}
@Test("Column spacing is accounted for in column count")
func columnSpacingAffectsCount() {
let noSpacing = makeFlowGrid(
minimumSize: 100, columnSpacing: 0, rowSpacing: 0, childSize: 100, count: 4)
let withSpacing = makeFlowGrid(
minimumSize: 100, columnSpacing: 50, rowSpacing: 0, childSize: 100, count: 4)
let (h1, _) = measureWidget(noSpacing, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 250)
#expect(h1 == 200, "250 width → 2 cols × 2 rows × 100 = 200")
let (h2, _) = measureWidget(
withSpacing, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 250)
#expect(h2 == 200, "250 width → 2 cols × 2 rows × 100 = 200")
}
@Test("Row spacing adds to total height")
func rowSpacingAddsHeight() {
let grid = makeFlowGrid(
minimumSize: 100, columnSpacing: 0, rowSpacing: 10, childSize: 100, count: 6)
let (minH, natH) = measureWidget(grid, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 300)
#expect(minH == 210, "2 rows × 100 + 1 spacing × 10 = 210")
#expect(natH == 210, "natural = 210")
}
@Test("Single child fills one cell")
func singleChild() {
let grid = makeFlowGrid(minimumSize: 200, childSize: 150, count: 1)
let (minH, natH) = measureWidget(grid, orientation: .GTK_ORIENTATION_VERTICAL, forSize: 400)
#expect(minH == 150, "single row = child height")
#expect(natH == 150, "natural = 150")
}
}