diff --git a/Package.swift b/Package.swift index 9022b6f..193727b 100644 --- a/Package.swift +++ b/Package.swift @@ -95,6 +95,15 @@ let package = Package( resources: [.process("Localized.yml")], plugins: [.plugin(name: "GenerateLocalized", package: "localized")] ), + + .testTarget( + name: "LuminateTests", + dependencies: [ + "CAspectContainer", + .product(name: "CAdw", package: "adwaita-swift"), + .product(name: "Adwaita", package: "adwaita-swift"), + ] + ), ], swiftLanguageModes: [.v5] ) diff --git a/Tests/LuminateTests/WidgetTests.swift b/Tests/LuminateTests/WidgetTests.swift new file mode 100644 index 0000000..180c356 --- /dev/null +++ b/Tests/LuminateTests/WidgetTests.swift @@ -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? { + 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?, + 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? { + let grid = flow_grid_new(Int32(minimumSize), Int32(columnSpacing), Int32(rowSpacing)) + for _ in 0..